Introduction
In our web applications, we often need to display a custom drop-down for user selection. In this article, we will focus on creating a custom drop-down list in Blazor WASM.
The most interesting part is that this custom drop-down is a Blazor component which will have its own UI and backend logic.
Once created, this can be easily used across applications in any number of razor pages, thus saving effort and allowing for maximum reusability.
Prerequisites
This article assumes you have a basic working knowledge of Blazor web assembly and .NET Core.
I will be using the API and Repository layer of my previous Blazor projects.
Kindly refer to the below articles to understand UI binding, database, and API interaction in Blazor apps.
Through this article, we will cover the following topics.
- How to create a custom drop-down component in Blazor?
- How to use a custom drop-down component on any razor pages?
- How to call the Data service in the Blazor app and get data using the Entity Framework core required for component binding?
Output
Region drop-down component
Implementation
Before implementation, you need to know the prerequisite.
- We need to create a new Blazor web assembly project with .NET Core hosted.
- The basic project structure is shown below.
- EmployeePortal.The client is Blazor Web Assembly Project
- EmployeePortal.The server is a .NET Core 3.1 Project Where our API and Database Repository would reside
- EmployeePoral.Shared is the .NET Standard Library Project which would hold the required Models for our project
Creating the dropdown component
Create a new Model class Region in the shared project as below.
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EmployeePortal.Shared {
[Table("RegionMaster")]
public class Region {
public int RegionId {
get;
set;
}
public string RegionName {
get;
set;
}
}
}
In the components folder of the client project add a new Razor and class file. The detailed structure of the client folder is mentioned below.
Add the below code in Regions.razor
@if (Regionmaster == null)
{
<p>
<em>Loading ...</em>
</p>
}
else
{
<select class="custom-select" @onchange="OnValueChanged" title="Region is required">
<option value="Select" selected disabled="disabled">(Choose Region)</option>
@foreach (var region in Regionmaster)
{
<option value="@region.RegionId">@region.RegionName</option>
}
</select>
}
It is very important to check if Regionsmaster is null else it will give an object reference error.
Add the below code in Regions.cs
When the component is getting initialized we are getting data for all the regions.
This Register Data service is already a registered program.cs of a client project.
builder.Services.AddHttpClient<IEmployeeDataService, EmployeeDataService>(
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
);
On drop-down value change, the OnValueChanged change will be fired and the event args will have its latest value.
Data Service would have the GetRegions function as below which would call the .NET Core API.
public async Task<IEnumerable<Region>> GetRegions()
{
return await JsonSerializer.DeserializeAsync<IEnumerable<Region>>(
await _httpClient.GetStreamAsync($"api/Employee/Regions"),
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }
);
}
In the .NET Core Project create a new API to retrieve Region data.
[HttpGet]
[Route("Regions")]
public IActionResult GetRegions()
{
return Ok(_employeeRepository.GetRegions());
}
The Employee Repository needs to be registered in startup.cs file of the .NET Core project.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddScoped<IEmployeeRepository, EmployeeRepository>();
SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
}
Repository Function to get Regions data from SQL database.
public IEnumerable<Region> GetRegions()
{
return _appDbContext.Regions.ToList();
}
The Regions dbset needs to be defined in the Entity framework db context class.
public class AppDbContext: DbContext {
public AppDbContext(DbContextOptions < AppDbContext > options): base(options) {}
public DbSet < Employee > Employees {
get;
set;
}
public DbSet < Region > Regions {
get;
set;
}
}
We have now completed with API repository and UI for our dropdown component.
Using our custom-created component on the home page
We can use our Regions component on AddEmployeeDialog.razor page.
@if (ShowDialog)
{
<div class="modal fade show d-block" id="exampleModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="titleLabel">Add Employee</h5>
<button type="button" class="close" @onclick="@Close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<EditForm Model="@Employee" OnValidSubmit="@HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group">
<label for="firstName">First name: </label>
<InputText id="firstName" class="form-control" @bind-Value="@Employee.FirstName" placeholder="Enter first name"></InputText>
<ValidationMessage For="@(() => Employee.FirstName)" />
</div>
<div class="form-group">
<label for="lastName">Last name: </label>
<InputText id="lastName" class="form-control" @bind-Value="@Employee.LastName" placeholder="Enter last name"></InputText>
<ValidationMessage For="@(() => Employee.LastName)" />
</div>
<div class="form-group">
<label for="email">Email: </label>
<InputText id="email" class="form-control" @bind-Value="@Employee.Email" placeholder="Enter email"></InputText>
<ValidationMessage For="@(() => Employee.Email)" />
</div>
<div class="form-group">
<label>Region: </label>
<Regions @bind-Value="RegionId" @ref="Regions"></Regions>
</div>
<button type="submit" class="btn btn-primary">Save employee</button>
<a class="btn btn-outline-primary" @onclick="@Close">Close</a>
</EditForm>
</div>
</div>
</div>
</div>
}
The selected region is again bound back in the RegionId property of the AddEmployeeDialog class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EmployeePortal.Shared;
using EmployeePortal.Client.Services;
using Microsoft.AspNetCore.Components;
namespace EmployeePortal.Client.Components {
public partial class AddEmployeeDialog: ComponentBase {
public Employee Employee {
get;
set;
} = new Employee {};
[Inject]
public IEmployeeDataService EmployeeDataService {
get;
set;
}
public bool ShowDialog {
get;
set;
}
[Parameter]
public string RegionId {
get;
set;
}
protected Regions Regions {
get;
set;
}
[Parameter]
public EventCallback < bool > CloseEventCallback {
get;
set;
}
public void Show() {
ResetDialog();
ShowDialog = true;
StateHasChanged();
}
public void Close() {
ShowDialog = false;
StateHasChanged();
}
private void ResetDialog() {
Employee = new Employee {};
}
protected async Task HandleValidSubmit() {
await EmployeeDataService.AddEmployee(Employee);
ShowDialog = false;
await CloseEventCallback.InvokeAsync(true);
StateHasChanged();
}
}
}
Thus we get the selected RegionId which can be saved into the database along with Employee Data.
Summary
Through this article, we have learned how to create a Custom Dropdown Blazor component model and use it in our razor pages. We also learned how the selected value is set in the page class property.
Thanks a lot for reading. I hope you liked this article. Please share your valuable suggestions and feedback. Write in the comment box in case you have any questions. Have a good day!