Build your first Blazor client-side application

In the previous article you learned to build your first Blazor server-side
application. In this article you will develop the same data entry form using
Blazor client-side application. Blazor client-side applications use WebAssembly
(Wasm) to run. A WebAssembly capable browser downloads the Blazor application on
the client-side and then executes it within the boundaries of the browser.
Let's get going.
The data entry form you will build in this article is shown below:

As you can see, the page displays a list of CustomerIDs from Customers table
of Northwind database. Selecting a CustomerID and clicking on the Show Data
button displays CompanyName, ContactName, and Country values of that customer.
You can modify these values and click on Update button to save the changes back
to the database.
Begin by creating a new Blazor client-side application in Visual Studio.

There are two project templates in Visual Studio that create a Blazor
client-side project - Blazor (client-side) and Blazor (ASP.NET Core hosted). The
former template creates a standalone Blazor client-side application that doesn't
talk with the server. The latter project template creates a Blazor client-side
application that is served by an ASP.NET Core application. Since the Blazor
application is hosted inside an ASP.NET Core application, you can call
server-side code such as Web APIs from the Blazor code. In our example you use a
Web API to perform the database update. So, you need to pick Blazor (ASP.NET
Core hosted) project template.
Take a look at the Solution Explorer once Visual Studio finishes creating the
projects. You will find three projects in the solution. The Client project is a
Blazor application that represents the client side UI of the application. The
Server project is an ASP.NET Core application that serves the Client application
to the browser. This project can also contain EF Core model, Web APIs and other
server side code. The Shared project contains code that can be shared between
the Client and the Server projects. In our specific example you don't need this
project as such.
Once the projects gets created. Add NuGet package for Entity Framework Core
to the Server project - Microsoft.EntityFrameworkCore.SqlServer.

Then add two classes that represent DbContext and Customer entity. These
classes are added to the Data folder and are shown below:
public class Customer
{
[Key]
[Required]
[StringLength(5, MinimumLength = 5)]
public string CustomerID { get; set; }
[Required]
[StringLength(30)]
public string CompanyName { get; set; }
[Required]
[StringLength(30)]
public string ContactName { get; set; }
[Required]
[StringLength(15)]
public string Country { get; set; }
}
public class AppDbContext:DbContext
{
public AppDbContext(
DbContextOptions<AppDbContext> options) :
base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
These classes are quite straightforward. Notice that the Customer class uses
data annotations for the sake of model validation.
To perform database update operations you need a Web API.
So, add a Web API controller class to the Server project's Controller folder. This class is shown below:
[Route("api/[controller]")]
public class CustomersController : ControllerBase
{
private AppDbContext db = null;
public CustomersController(AppDbContext db)
{
this.db = db;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
List<Customer> data = await
db.Customers.ToListAsync();
return Ok(data);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetAsync
(string id)
{
Customer cust = await
db.Customers.FindAsync(id);
return Ok(cust);
}
[HttpPut("{id}")]
public async Task<IActionResult> PutAsync
(string id, [FromBody]Customer cust)
{
if (ModelState.IsValid)
{
db.Customers.Update(cust);
await db.SaveChangesAsync();
return Ok
("\"Customer updated successfully!\"");
}
else
{
return BadRequest
("\"Error while updating customer!\"");
}
}
}
The CustomersController class contains three methods - GetAsync(), GetAsync(id),
and PutAsync(). These methods perform the respective operations. Once developed
the AppDbContext needs to be registered with the DI
container. This is done in the ConfigureServices() of the Startup class (make
sure to do this in Server project).
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("data source=.;
initial catalog=Northwind;integrated security=true;"));
}
You register AppDbContext with the DI container using AddDbContext() method.
Next, go to the Client project and add Customer class to its Data folder. We
need this Customer class in the client application because the Web API from the
Server application is going to return data wrapped in this data structure. You
could have isolated the Customer class into Shared project but for the sake of
simplicity you can add it to the Client project itself. Make sure that the
Customer class has data annotations for performing model validation.
Then add a new Razor Component named Customers.razor to the Pages folder.

Open Customers.razor file and following code at the top:
@page "/customers"
@using BlazorDemo.Client.Data
@inject HttpClient http
The @page directive controls the route at which this component can be
accessed. In this case the component is accessible at this URL :
http://localhost:1234/customers. The @using directives uses BlazorDemo.Client.Data namespace so
that model classes can be referenced in the code. Notice the @inject directive
that injects an instance of HttpClient into the component. You need HttpClient
object to invoke the Customers Web API.
Now, add the following code to the @code block.
@code {
Customer cust = new Customer();
List<Customer> customers = null;
string Message { get; set; }
}
As you might have guessed the @code block is used to write C# code (members,
properties, methods) that interacts with the user interface. Here, the code
declares a member of type Customer. This member is used during data binding with
the form (discussed shortly). The List<Customer> stores a list of customers
returned from GetAsync() method of the Web API and is also used to fill the dropdown list. The
Message property is used to notify user of the result of database update
operation.
As soon as the component is initialized you need to call the GetAsync()
method of the Web API so that the CustomerID dropdown list can be populated. To do this, you
write OnInitAsync() method in @code block as shown below:
protected override async Task OnInitAsync()
{
customers = await http.GetJsonAsync
<List<Customer>>("/api/customers");
}
You are basically overriding OnInitAsync() method of ComponentBase class (Blazor
components inherit from ComponentBase class). Inside, you call GetJsonAsync()
method of HttpClient to invoke the GetAsync() action of the Web API. The data
returned by the GetAsync() action is stored into customers member.
If you observe the user interface shown at the beginning of this article, you
will find that there are two buttons - Show Data and Update. You need click
event handlers for these buttons.
private async Task OnShowDataAsync()
{
cust = await http.GetJsonAsync
<Customer>("/api/customers/" + cust.CustomerID);
}
private async Task OnUpdateAsync()
{
Message = await http.PutJsonAsync
<string>("/api/customers/" + cust.CustomerID, cust);
}
You will be binding cust member with the data entry form. The data binding
features of Blazor allow you to do one-way and two-way data binding. When you
select a CustomerID the CustomerID property of cust member is automatically
changed to reflect the selection. So, when you pass cust.CustomerID in the
GetJsonAsync() call you are passing the selected value. The returned Customer
object's values are reflected in the other textboxes. The OnUpdateAsync() event
handler simply calls the PutJsonAsync() method of HttpClient to save the changes
to the database.
Below the @code block write the following Blazor markup.
<h2>Customer Manager</h2>
<EditForm Model="cust">
<DataAnnotationsValidator>
</DataAnnotationsValidator>
<table border="0" cellpadding="20">
<tr>
<td>Customer ID :</td>
<td>
<InputSelect id="customerid"
@bind-Value="@cust.CustomerID">
@if (customers != null)
{
foreach (var c in customers)
{
<option value="@c.CustomerID">
@c.CustomerID</option>
}
}
</InputSelect>
<button type="button"
@onclick="@OnShowData">Show Data</button>
</td>
</tr>
<tr>
<td>Company Name :</td>
<td>
<InputText id="companyname"
@bind-Value="@cust.CompanyName" />
<ValidationMessage
For="@(() => cust.CompanyName)" />
</td>
</tr>
<tr>
<td>Contact Name :</td>
<td>
<InputText id="contactname"
@bind-Value="@cust.CompanyName" />
<ValidationMessage
For="@(() => cust.ContactName)" />
</td>
</tr>
<tr>
<td>Country :</td>
<td>
<InputText id="country"
@bind-Value="@cust.Country" />
<ValidationMessage
For="@(() => cust.Country)" />
</td>
</tr>
<tr>
<td colspan="2">
<button type="button"
@onclick="@OnUpdate">Update</button>
</td>
</tr>
</table>
<ValidationSummary></ValidationSummary>
</EditForm>
<h5>@Message</h5>
The above markup shows several Blazor features including data binding,
components, data annotations and validations.
The <EditForm> component is used to render a data entry <form>. The Model
property sets the model for the form to an object (Customer object in this
case). The <DataAnnotationsValidator> component enables form validations based
on data annotation attributes such as [Required] and [StringLength].
The CustomerID dropdown list is rendered using an <InputSelect> component
(same as <select> HTML element). To bind the dropdown list with model's property
@bind-Value attribute performs two-way data binding (from cust to dropdown and
from dropdown to cust). To populate all the CustomerIDs into the dropdown list
C# foreach loop is used. The onclick event handler of Show Data button is wired
to OnShowData() method written in the @code block.
To display CompanyName, ContactName, and Country <InputText> component is
used. This component renders HTML <input> element. Two-way data binding is done
as before. To display field level validation errors <ValidationMessage>
component is used. The onclick event of Update button is handled by
OnUpdate() method. At the bottom, there is <ValidationSummary> component that
displays a collective list of error messages.
Finally, the Message property is displayed so that use known the outcome of
update operation.
Compile the project and run the Server application by pressing F5. Once the browser
window loads the application, go to the address bar and change the URL to
http://localhost:1234/customers. You should now see the UI of the Customers
component. Check whether data can be updated or not.
That's it for now! Keep coding!!