Creating Custom Drop Down List Component In Blazor 3.2 WASM

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 intresting 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 reusablity.
 
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.
  • https://www.c-sharpcorner.com/article/blazor-web-assembly-3-2-addeditdelete-full-functional-application-part-1-crud/
  • https://www.c-sharpcorner.com/article/blazor-web-assembly-3-2-addeditdelete-fully-functional-application-part-2/ 
Through this article, we will cover the following topics,
  • How to create custom drop down component in Blazor?
  • How to use custom drop down component on any razor pages?
  • How to call Data service in Blazor app and get data using Entity Framework core required for component binding?
Output
 
Region drop down component
 
Creating Custom Drop Down List Component In Blazor 3.2 WASM

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 as shown below.
 
Creating Custom Drop Down List Component In Blazor 3.2 WASM
  • EmployeePortal.Client is Blazor Web assembly Project
  • EmployeePortal.Server is .NET Core 3.1 Project Where our API and Database Repository would reside
  • EmployeePoral.Shared is .NET Standard Library Project which would hold the required Models for our project

Creating the dropdown component

 
Create a new Model class Region in shared project as below.  
  1. using System;  
  2. using System.ComponentModel.DataAnnotations;  
  3. using System.ComponentModel.DataAnnotations.Schema;  
  4. namespace EmployeePortal.Shared {  
  5.     [Table("RegionMaster")]  
  6.     public class Region {  
  7.         public int RegionId {  
  8.             get;  
  9.             set;  
  10.         }  
  11.         public string RegionName {  
  12.             get;  
  13.             set;  
  14.         }  
  15.     }  
  16. }  
In components folder of client project add a new Razor and class file.The detailed structure of client folder is mentioned below.
 
Creating Custom Drop Down List Component In Blazor 3.2 WASM
 
Add the below code in Regions.razor
  1. @if(Regionmaster == null)  
  2. {  
  3. <p>  
  4.     <em>Loading ...</em>  
  5. </p>  
  6. }  
  7. else  
  8. {  
  9. <select class="custom-select" @onchange="OnValueChanged" title="Region is required ">  
  10.     <option value="Select" selected disabled="disabled">(Choose Region)</option>  
  11. @foreach (var region in Regionmaster)  
  12. {  
  13.     <option value="@region.RegionId"> @region.RegionName</option>  
  14. }  
  15. </select>  
  16. }  
It is very important to check if Regionsmaster is null else it will give object reference error.
 
Add the below code in Regions.cs
  • This is our component class which would invoke EmployeeDataservice
  • Which would in turn call .NET Core API using System
  1. public partial class Regions: ComponentBase {  
  2.     [Parameter]  
  3.     public string RegionId {  
  4.         get;  
  5.         set;  
  6.     }  
  7.     public IEnumerable < Region > Regionmaster {  
  8.             get;  
  9.             set;  
  10.         }  
  11.         [Inject]  
  12.     public IEmployeeDataService EmployeeDataService {  
  13.         get;  
  14.         set;  
  15.     }  
  16.     [Parameter]  
  17.     public string Value {  
  18.         get;  
  19.         set;  
  20.     }  
  21.     [Parameter]  
  22.     public EventCallback < string > ValueChanged {  
  23.         get;  
  24.         set;  
  25.     }  
  26.     protected async override Task OnInitializedAsync() {  
  27.         Regionmaster = (await EmployeeDataService.GetRegions()).ToList();  
  28.     }  
  29.     private Task OnValueChanged(ChangeEventArgs e) {  
  30.         Value = e.Value.ToString();  
  31.         return ValueChanged.InvokeAsync(Value);  
  32.     }  
  33. }  
When the component is getting initialzed we are getting data for all the regions.
 
This Register Data service in already registered program.cs of client project 
  1. builder.Services.AddHttpClient<IEmployeeDataService, EmployeeDataService>(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));  
On drop down value change, the OnValueChanged changed will be fired and the event args will have its latest value.
 
Data Service would have GetRegions function as below which would call the .NET Core API 
  1. public async Task<IEnumerable<Region>> GetRegions()  
  2. {  
  3.    return await JsonSerializer.DeserializeAsync<IEnumerable<Region>>  
  4.    (await _httpClient.GetStreamAsync($"api/Employee/Regions"), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });  
  5. }  
In .NET Core Project create a new API to retrive Region data 
  1. [HttpGet]  
  2. [Route("Regions")]  
  3. public IActionResult GetRegions()  
  4. {  
  5.    return Ok(_employeeRepository.GetRegions());  
  6. }  
The Employee Repository needs to be registered in startup.cs file of .NET Core project
  1. public void ConfigureServices(IServiceCollection services) {  
  2.     services.AddDbContext < AppDbContext > (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));  
  3.     services.AddControllersWithViews();  
  4.     services.AddRazorPages();  
  5.     services.AddScoped < IEmployeeRepository, EmployeeRepository > ();  
  6.     SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");  
  7. }  
Repository Function to get Regions data from SQL database
  1. public IEnumerable<Region> GetRegions()  
  2. {  
  3.    return _appDbContext.Regions.ToList();  
  4. }  
The Regsions dbset needs to be defined in Entity framework db context class.
  1. public class AppDbContext: DbContext {  
  2.     public AppDbContext(DbContextOptions < AppDbContext > options): base(options) {}  
  3.     public DbSet < Employee > Employees {  
  4.         get;  
  5.         set;  
  6.     }  
  7.     public DbSet < Region > Regions {  
  8.         get;  
  9.         set;  
  10.     }  
  11. }  
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 
  1. @if (ShowDialog)  
  2. {  
  3.   
  4. <div class="modal fade show d-block" id="exampleModal" tabindex="-1" role="dialog">  
  5.     <div class="modal-dialog" role="document">  
  6.         <div class="modal-content">  
  7.             <div class="modal-header">  
  8.                 <h5 class="modal-title" id="titleLabel">Add Employee</h5>  
  9.                 <button type="button" class="close" @onclick="@Close" data-dismiss="modal" aria-label="Close">  
  10.                     <span aria-hidden="true">×</span>  
  11.                 </button>  
  12.             </div>  
  13.             <div class="modal-body">  
  14.                 <EditForm Model="@Employee" OnValidSubmit="@HandleValidSubmit">  
  15.                     <DataAnnotationsValidator />  
  16.                     <ValidationSummary />  
  17.                     <div class="form-group">  
  18.                         <label for="firstName">First name: </label>  
  19.                         <InputText id="firstName" class="form-control" @bind-Value="@Employee.FirstName" placeholder="Enter first name"></InputText>  
  20.                         <ValidationMessage For="@(() => Employee.FirstName)" />  
  21.                     </div>  
  22.                     <div class="form-group">  
  23.                         <label for="lastName">Last name: </label>  
  24.                         <InputText id="lastName" class="form-control" @bind-Value="@Employee.LastName" placeholder="Enter last name"></InputText>  
  25.                         <ValidationMessage For="@(() => Employee.LastName)" />  
  26.                     </div>  
  27.                     <div class="form-group">  
  28.                         <label for="email">Email: </label>  
  29.                         <InputText id="email" class="form-control" @bind-Value="@Employee.Email" placeholder="Enter email"></InputText>  
  30.                         <ValidationMessage For="@(() => Employee.Email)" />  
  31.                     </div>  
  32.                     <div class="form-group">  
  33.                         <label>Region: </label>  
  34.                         <Regions @bind-Value="RegionId" @ref="Regions"></Regions>  
  35.                     </div>  
  36.                     <button type="submit" class="btn btn-primary">Save employee</button>  
  37.                     <a class="btn btn-outline-primary" @onclick="@Close">Close</a>  
  38.                 </EditForm>  
  39.             </div>  
  40.         </div>  
  41.     </div>  
  42. </div>  
The selected region is again bound back in RegionId property of the AddEmployeeDialog class
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Threading.Tasks;  
  5. using EmployeePortal.Shared;  
  6. using EmployeePortal.Client.Services;  
  7. using Microsoft.AspNetCore.Components;  
  8. namespace EmployeePortal.Client.Components {  
  9.     public partial class AddEmployeeDialog: ComponentBase {  
  10.         public Employee Employee {  
  11.             get;  
  12.             set;  
  13.         } = new Employee {};  
  14.         [Inject]  
  15.         public IEmployeeDataService EmployeeDataService {  
  16.             get;  
  17.             set;  
  18.         }  
  19.         public bool ShowDialog {  
  20.             get;  
  21.             set;  
  22.         }  
  23.         [Parameter]  
  24.         public string RegionId {  
  25.             get;  
  26.             set;  
  27.         }  
  28.         protected Regions Regions {  
  29.             get;  
  30.             set;  
  31.         }  
  32.         [Parameter]  
  33.         public EventCallback < bool > CloseEventCallback {  
  34.             get;  
  35.             set;  
  36.         }  
  37.         public void Show() {  
  38.             ResetDialog();  
  39.             ShowDialog = true;  
  40.             StateHasChanged();  
  41.         }  
  42.         public void Close() {  
  43.             ShowDialog = false;  
  44.             StateHasChanged();  
  45.         }  
  46.         private void ResetDialog() {  
  47.             Employee = new Employee {};  
  48.         }  
  49.         protected async Task HandleValidSubmit() {  
  50.             await EmployeeDataService.AddEmployee(Employee);  
  51.             ShowDialog = false;  
  52.             await CloseEventCallback.InvokeAsync(true);  
  53.             StateHasChanged();  
  54.         }  
  55.     }  
  56. }  
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 Custom Drop down Blazor component model and used it in our razor pages. We also learned how the selected value is set in 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!