October 2018

Volume 33 Number 10

Cutting Edge - Blazor at Work: Events, Binding and Composition

By Dino Esposito

Dino EspositoI confess I’ve never been a huge fan of Angular and React, two extremely popular frameworks for writing modern Web front ends. I don’t want to spark any heated debates, but I think that it’s safe to state that it’s all about how much work the application does on the server and how much it does on the client. Clearly, any solution centered on the client side of the Web has the potential to be more responsive and, maybe more importantly, much less impactful on the server. On the other hand, there’s the learning curve and extra programming work that has to be done with JavaScript or, at best, TypeScript.

Blazor is an interesting and warmly welcomed attempt to enable developers to build client-centric applications using, for the most part, C# and the .NET runtime. As Blazor evolves, more and more features will be added to make it provide nearly the same set of capabilities as Angular, React or Vue. In the end, writing a Blazor application will be a matter of stitching together HTML and CSS using C# code, the Razor template language, and JSON data downloaded from some HTTP endpoints.

The killer feature of Blazor is that an object-oriented language like C# is much easier to work with than JavaScript when expressing even moderately sophisticated business logic. In addition, Blazor offers a client-centric programming environment that’s much more familiar than, say, Angular is to the community of ASP.NET developers.

It will take time for Blazor to mature and compete with established frameworks, but the dream of C# code running in the browser via an open technology (WebAssembly) rather than a proprietary plug-in is alive.

In a previous column (msdn.com/magazine/mt829756), I presented the highlights of the Blazor architecture and presented a basic example based on a digital clock and a timer to update it. In this column, I move toward the type of core tasks you’d expect to perform in a client-centric application. This demo will use UI components, parameters, events, JavaScript interoperability and data binding. Figure 1 offers a glimpse of the final result: a form that uses auto-completion to accept a string, runs a query on the back end, gets JSON data and refreshes the UI.

The Sample Application in Action
Figure 1 The Sample Application in Action

Creating the Project

The sample project is built as an ASP.NET Core-hosted application. The solution is made of three projects—the client Blazor project, the Web API server for remote operations and an optional shared library linked to both projects. This shared library is the ideal location for all data-transfer objects and view models used on both the client and the server. The default configuration of the solution in Visual Studio sets the ASP.NET Core project as the runner.

Let’s take a look at the content of the startup class. In addition to the canonical stuff to set up the MVC router, error handling, logging, compression and whatever else you may need, it has ad hoc middleware to use Blazor:

public void Configure(IApplicationBuilder app)
{
  app.UseResponseCompression();
    ...
  app.UseBlazor<Client.BlazorProgram>();
}

The type passed as an argument to the middleware is the type of the program entry point in the Blazor application. Needless to say, the ASP.NET Core server application (acting as a plain Web API) must have a reference to the Blazor project. Though it’s not strictly a requirement, it is recommended that the application uses compression to reduce the amount of traffic over the network. The compression service is configured in the ConfigureServices method of the startup class as:

services.AddResponseCompression(options =>
{
  options.MimeTypes =
    ResponseCompressionDefaults.MimeTypes.Concat(new[]
    {
      MediaTypeNames.Application.Octet,
      WasmMediaTypeNames.Application.Wasm,
    });
});

You don’t need to have anything else in the Web API project other than the layers of code necessary to retrieve and serve JSON data to the client Blazor front end. The Web API used in this article returns information about countries of the world. The list of countries is embedded in the project as an internal JSON resource, and a repository class takes care of caching and filtering its content (see Figure 2). The Shared library defines the Country class and its ancillary types. As mentioned, these types will be shared between the Web API and the Blazor application.

The Sample Project in Visual Studio
Figure 2 The Sample Project in Visual Studio

UI Components of a Blazor Application

Realistically, any Blazor application consists of a bunch of client-side navigable Web pages, each of which is written in Razor. Ultimately, a sample .cshtml page consists of a bit of HTML, some Razor extensions, and custom components similar to the custom tag helpers of ASP.NET Core. UI components are distinct segments of Razor code that at first may resemble the partial views of ASP.NET Core applications. At a deeper look, though, it turns out that components are pretty different from partial views. Components take parameters, are stateful, have a specific lifecycle, can define events and have a different compilation model.

Let’s say you have a CountrySelector.cshtml file somewhere in your project. A common location is the Views/Shared folder of the Blazor application, but the folder isn’t really important. Aside from some possible CSS style and HTML layout information, the component will contain an input field and a button, as shown here:

<input type="text" 
  bind="@SelectedCountry"
  placeholder="@Placeholder" />
<button type="button" onclick="@GetCountryInfo">
  <i class="fa fa-search"></i>
</button>

When the button is clicked, the GetCountryInfo method runs. The value displayed in the textbox is associated with a component-­level field named SelectedCountry. Here’s the minimal code you need to have in the component to handle the click and query the Web API:

@functions
{
  private string SelectedCountry;
  async void GetCountryInfo()
  {
    var url = WebUtility.UrlEncode($"/country/search/{SelectedCountry}");
    var countries = await
      Http.GetJsonAsync<IList<Country>>(url);
  }
}

The Bind attribute used on the INPUT element ensures two-way data binding between the value attribute of the DOM element and the SelectedCountry field. The binding is automatic on the onchange event.

There are a few interesting things to remark on about UI components. For example, a UI component can expose parameters that the parent sets. Here’s how the sample page of Figure 1 references the CountrySelector UI component:

<CountrySelector
  uniqueId="cs"
  placeholder="Start typing the country name"
  typeAheadUrl="/hint/countries"
  onSelectionMade="@PopulateGrid" />

Note that the case of attributes is a matter of preference. You can use the client-side common camelCase style (as shown in this snippet) or go with PascalCase style to make it clear that those attributes fall in the .NET land.

All attributes listed are defined as parameters in the @functions{…} block of the UI component, like so:

[Parameter]
string UniqueId { get; set; } = "country-selector";
[Parameter]
string TypeAheadUrl { get; set; } = "";
[Parameter]
string Placeholder { get; set; } = "Country";
[Parameter]
Action<IList<Country>> OnSelectionMade { get; set; }

Most of the parameters set in the sample CountrySelector are used to customize the HTML being output. For example, the Placeholder attribute sets the helper text in an empty textbox, and the TypeAheadUrl attribute sets the URL that ultimately provides auto-completion. (More on this in a moment.)

Firing and Handling Events

The OnSelectionMade parameter is ultimately an event handler, set by the parent component to specify the code to execute in reaction to an event fired by the CountrySelector component. In its simplest form, an event is defined as an Action<T> delegate. In the code snippet I just showed you, the OnSelectionMade event provides the handler with a list of countries to process:

[Parameter]
Action<IList<Country>> OnSelectionMade { get; set; }

Let’s find out how the event is fired from within a component. The sample CountrySelector component has two main purposes: capturing a string of text to be used as the argument of a query, and running the query against a remote HTTP endpoint. The list of countries received from the query is exposed to whom it may concern through the OnSelectionMade custom event, like so:

async void GetCountryInfo()
  {
    var url = $"/country/search/{SelectedCountry}";
    var countries = await
      Http.GetJsonAsync<IList<Country>>(url);            
    OnSelectionMade?.Invoke(countries);
  }

If not null, the Action<T> delegate is invoked with the list of countries. In general, a good practice is wrapping the event invocation code in a general-purpose and parameter-less method like NotifyState­Changed. In this case, it might be necessary that the state to be notified is captured in a centralized data structure.

Let’s focus now on how an event can be handled from a parent component, such as the host page. The main page rendered in Figure 1 contains the following @functions block:

@functions
{
  public string Message = "";
  IList<Country> Countries = new List<Country>();
  private void PopulateGrid(IList<Country> countries)
  {
    Message = $"{countries.Count} countries found.";
    Countries = countries;
    StateHasChanged();
  }
}

The method PopulateGrid is invoked when the event OnSelectionMade is fired. The method receives the list of selected countries and saves it internally to the Countries field. In addition, the total number of countries is saved to the Message field. Both fields, Countries and Message, are bound to a Blazor component (CountryGrid) and an HTML SPAN element, like so:

<CountrySelector onSelectionMade="@PopulateGrid" />
<span class="badge badge-primary">@Message</span>
<CountryGrid dataSet="@Countries" />

The content of the CountryGrid Blazor component is a plain HTML table-based template dynamically populated around the content of its DataSet parameter:

@functions
{
  [Parameter]
  IList<Country> DataSet { get; set; } = new List<Country>();
}

Having the event handler set the Countries field to a new value doesn’t automatically refresh any components in the current view. If state updates occur because of an explicit user action, the UI is refreshed automatically. Otherwise, to refresh the view, an explicit, programmatic call to the StateHasChanged method is required:

void PopulateGrid(IList<Country> countries)
{
  Countries = countries;
  StateHasChanged();
}

Foundation of Data-Bound Components

Any client-centric Web framework spends a large share of its uptime doing data binding, and most of the time bindable data comes as JSON strings from some remote HTTP endpoint. Fetching data, whether customer details, flights or weather forecasts, is a fairly common scenario. To download data, you need an HTTP client referenced in the component. The easiest way to do that is using dependency injection, like this:

@inject HttpClient Http

The symbol Http references a singleton instance of HttpClient that can be used to get a JSON feed and serialize it to C# collections and classes. Here’s the code you use to get bindable JSON data from a remote URL in Blazor:

var countries = await Http.GetJsonAsync<IList<Country>>(url);

Once you have the data, you need to find a way to bind it to visual elements. At present, in Blazor you don’t reference UI components (or HTML elements) by name and code against their programming interface. Instead, you expose a public event, receive data through it and use component parameters to pass data to child components. In the previous example, the CountryGrid component has no ID property assigned and no instance of it is programmatically referenced to receive the data to bind. Instead, a parent field is assigned (Countries) that was data-bound to the DataSet property of the CountryGrid component.

JavaScript Interoperability

Blazor allows you to use C# code to implement client-side operations. However, the UI (both layout and style) is still expressed in HTML and CSS with some JavaScript that may be required here and there. Blazor doesn’t mandate JavaScript interoperability, but realistically, some form of interoperation may be necessary at times. One scenario is to integrate in a Blazor UI some existing, and fairly sophisticated, pieces of JavaScript code.

As an example, let’s consider the popular typeahead.js library for auto-completion. The library comes as a jQuery plug-in that you programmatically connect to an input field. Upon loading, the plug-in must be invoked to react to the input event fired by the DOM as the user types in the field. How would you call JavaScript from within a Blazor view? As of Blazor 0.5.0, you need to wrap up any JavaScript code you wish to invoke from .NET in a top-level function registered on the browser’s window object, like this:

window.MyFunctions = {
  typeAhead = function() {
    $(document).ready(function() {      // Typeahead initialization code
    });
  },
  uxDebug = function() {
    $(document).ready(function() { ... });
  }
}

You place similar JavaScript code in a file referenced in the main index.html file, right after the Blazor bootstrap SCRIPT tag. The browser’s window object is only decorated with custom methods, but still none of them are called. To enable typeahead.js, you invoke the JavaScript function from within the OnInit method of the Blazor component. Hence, the CountrySelector component will contain the following:

protected override void OnInit()
{
  JSRuntime.Current.InvokeAsync<Task>(
    "MyFunctions.typeAhead", "#input-field",
    TypeAheadUrl);
}

As a result, the JavaScript wrapper for typeAhead is invoked as soon as the Blazor view is rendered and, because of the implementation, its effect will be bound to the DOM’s ready event. Figure 3 shows what the typeAhead library looks like imple­mented in an app UI.

Typeahead in Action
Figure 3 Typeahead in Action

The black ribbon visible at the top of the Web page in Figure 3 is a relatively simple piece of JavaScript that uses the services of the WURFL.JS endpoint (wurfl.io) to collect reliable browser infor­mation. Here, it shows the current Bootstrap size, actual width and some browser information.

Wrapping Up

Blazor promotes a component-based UI with data binding being the primary tool to glue together data and layout elements. HTML and CSS are the underlying languages to express visuals. JavaScript is fully supported, but in general, complex UI elements are better implemented using framework-specific native components.


Dino Esposito has authored more than 20 books and 1,000-plus articles in his 25-year career. Author of “The Sabbatical Break,” a theatrical-style show, Esposito is busy writing software for a greener world as the digital strategist at BaxEnergy. Follow him on Twitter: @despos.

Thanks to the following Microsoft technical expert for reviewing this article: Daniel Roth


Discuss this article in the MSDN Magazine forum