January 2019

Volume 34 Number 1

[Cutting Edge]

Template-Based Components in Blazor

By Dino Esposito | January 2019

Dino EspositoIt’s been nearly a year since the first public build of Blazor was released in early 2018. Designed to be a client-side Web framework capable of running C# and .NET code from within the host browser, the platform has evolved in a number of directions.

Blazor remains a plain client-side framework processing data downloaded from any reachable back-end services, but it also had its underpinnings modified to run entirely from the server through a SignalR connection. The recent introduction of the SignalR Azure service just confirms, in my opinion, Microsoft’s intention of pushing Blazor more and more as a modern development platform. A scalable cloud service between the server application and myriad clients guarantees that .NET Core code can be effectively hosted on the server and interactively run on the client through the inter­mediation of C# instead of JavaScript.

As a client-side Web framework, Blazor (part of which will ship with the upcoming ASP.NET Core 3.0 release) can’t do its job well without components. In version 0.6.0 of the framework, the Blazor team introduced a particular flavor of components: template-based. In this article, I’ll explore how they work by updating the type-ahead example featured in my past two columns to be a template-based component.

Adding Templates to Blazor

Simple components can be configured through properties, but realistic components often need more rendering flexibility, and templates are a canonical way to achieve that. For example, think of a data grid component. With Razor, or any other data-binding infrastructure, you can easily build a data table linked to a known data source. Here’s a quick example of how to build an HTML table out of a collection of data items:

<table>
@foreach(var item in Items)
{
  <tr>
    <td>@item.FirstName</td>
    <td>@item.LastName</td>
  </tr>
}
</table>

It’s quick and easy, but there’s not much there that can be reused. Now imagine you make the grid structure richer by adding a header, a footer and perhaps a search bar on top and a pager bar at the bottom. Graphical layout and code behind both the search bar and the pager bar won’t change with the actual data being searched and paged. Yet you’re going to rewrite search and pager bar each and every time you use a data grid to display a different type of data.

With this minimal level of abstraction, a grid of countries and a grid of customers are completely different entities, although the core code behind the various internal parts is nearly the same. Template-based components address this specific scenario and show a way to have a single DataGrid component capable of presenting, searching, and paging both countries and customers with a single codebase.

Let’s say you want to have a rich grid component in your Blazor front-end views. Figure 1 shows the source code of a brand new template-based DataSource Blazor component.

Figure 1 The DataSource Template Component

@typeparam TItem
@inject HttpClient HttpExecutor
<div style="border: solid 4px #111;">   
  <div class="table-responsive">
    <table class="table table-hover">
      <thead>
        <tr>
          @if (HeaderTemplate != null)
          {
            @HeaderTemplate
          }
        </tr>
      </thead>
      <tbody>
        @foreach (var item in Items)
        {
          <tr>
           @RowTemplate(item)
          </tr>
        }
      </tbody>
      <tfoot>
        <tr>
          @if (FooterTemplate != null)
          {
            @FooterTemplate(Items)
          }
        </tr>
      </tfoot>
    </table>
  </div>
</div>
@functions {
  [Parameter]
  RenderFragment HeaderTemplate { get; set; }
  [Parameter]
  RenderFragment<TItem> RowTemplate { get; set; }
  [Parameter]
  RenderFragment<IList<TItem>> FooterTemplate { get; set; }
  [Parameter]
  IList<TItem> Items { get; set; }
}

As you can see, the DataSource component is built around the skeleton of an HTML table in which the body is constructed by iterating table rows on top of data records in the bound collection. The header and footer are defined as table rows, with the details of the actual content left to client pages. Client pages can interact and further customize the component through the public interface defined via the @functions section. There you’ve defined three templates—HeaderTemplate, FooterTemplate and RowTemplate—and a property, called Items, that act as the actual data source and data provider for the component.

You can have template-based components bound to a fixed data type, or you can have components bound to a generic data type that’s specified declaratively. The same grid component can realistically be used to present, search and page different collections of data. To endow the component with this capability, in Blazor you use the @typeparam directive, like so:

@typeparam TItem

Any reference to the TItem moniker found in the Razor source code is treated as a reference to the dynamically determined type of a C# generic class. You specify the actual type being used within the component through the TItem property. Figure 2 shows how a client page would declare a DataSource template component.

Figure 2 Declaring a DataSource Template Component

<DataSource Items="@Countries" TItem="Country">
  <HeaderTemplate>
    <th>Name</th>
    <th>Capital</th>
  </HeaderTemplate>
  <RowTemplate>
    <td>@context.CountryName</td>
    <td>@context.Capital</td>
  </RowTemplate>
  <FooterTemplate>
    <td colspan="2">
      @context.Count countries found.
    </td>
  </FooterTemplate>
</DataSource>

The name of the generic type property—TItem in the previous code snippet—will match the name of the type parameter as declared through the @typeparam directive within the source code of the component. Note that frequently the generic type parameter is just inferred by the framework and may not be specified.

Programming Aspects of Templates

A Blazor template is an instance of the RenderFragment type. Put another way, it’s a chunk of markup being rendered by the Razor view engine that can be treated like a plain instance of a .NET type. Most templates are parameter-less, but you can make them generic, too. A generic template will receive an instance of the specified type as an argument and can use that content to render its output. In the sample in Figure 2, the header template is parameter-less, but the row and the footer templates are generic.

In particular, the RowTemplate property takes an instance of TItem whereas the FooterTemplate property receives a collection of TItem instances. If needed, you can also define a template to receive an instance of a fixed type. For example, the FooterTemplate could instead be passed only an integer denoting the number of items being rendered in the page. This is shown in the code here:

RenderFragment<TItem> RowTemplate { get; set; }
RenderFragment<IList<TItem>> FooterTemplate { get; set; }

A template can be made optional to implement in a client page, by wrapping its call with a plain check for existence before use. Here’s how a Blazor component can make one of its template properties optional:

@if (FooterTemplate != null)
{
  @FooterTemplate(Items)
}

When rendering a parametric template, you use the “context” implicit name to reference the argument of the template. For example, when rendering a table row through the RowTemplate property, you use the context parameter to refer to the item being rendered, like so:

<RowTemplate>
  <td>@context.CountryName</td>
  <td>@context.Capital</td>
</RowTemplate>

Note that the name of the context argument can be declaratively changed through the Context property of the template, as shown here:

<RowTemplate Context="dataItem">
  <td>@dataItem.CountryName</td>
  <td>@dataItem.Capital</td>
</RowTemplate>

As a result, the DataSource generic component can be used in the same Blazor view to populate data grids of different data types, as depicted in Figure 3. The structure of the code you write is summarized like this:

<DataSource Items="@Countries" TItem="Country">
  ...
</DataSource>
<DataSource Items="@Forecasts" TItem="WeatherForecast">
  ...
</DataSource>

DataSource Generic Component
Figure 3 DataSource Generic Component

Any surrounding markup and code you may have around the displayed grid (for example, pager bar, search bar, sorting buttons) are fully reused. As a personal note, this deeper level of markup customization reminds me of the old days of ASP.NET Web Forms, where custom server controls, via templates and custom properties, defined their own domain-specific language. This made the process of outlining the desired UI quite smooth and fluent. The advent of MVC and the subsequent shift to plain, client-side Web development brought us closer to the HTML machinery and away from abstraction. Framework components are just trying to recover that level of expressivity.

Rewriting the TypeAhead Blazor Component

Last month (msdn.com/magazine/mt830376) I presented a type-ahead component entirely written in Blazor providing the same functionality as the popular—and JavaScript-based—Twitter TypeAhead plug-in. In that implementation, the server endpoint in charge of returning hints was actually forced to return a compact and general-purpose data transfer object with three properties: the unique ID of the object identified in the query, a display text, and a third property rendering of the markup to show in the dropdown box for each hint.

In last month’s demo, I used the type-ahead component to find a country name. The server endpoint returned an object made of the ISO country code, the country name, and an HTML snippet with country name, capital, and continent. The HTML snippet, however, was under the full control of the server implementation and there was no chance for the author of the client page using the type-ahead component to control the layout of the HTML snippet. That’s just the perfect scenario to see template-based components in action outside the realm of a basic demo as I’ve done so far.

Figure 4 presents the HTML layout of the new type-ahead component. It’s the same code from last month, with a notable exception: the format of the data returned by the hint provider. In the original implementation, the hint provider (a sample controller) returned a data transfer object and therefore was the single point of control of the actual data being selected by the user. Having the hint provider return a list of countries instead of a list of tailor-made type-ahead items enables the Blazor component to expose an ItemTemplate property for callers to decide the layout of each dropdown menu item. Receiving a list of countries enables the component to raise its OnSelection event, passing on directly the instance of the selected data item to any interested listeners.

Figure 4 A Template-Based TypeAhead Component

<div>
  <div class="input-group">
    <input type="text" class="@Class"
           oninput="this.blur(); this.focus();"
           bind="@SelectedText"
           onblur="@(ev => TryAutoComplete(ev))" />
    <div class="input-group-append">
      <button class="btn dropdown-toggle"
              type="button"
              data-toggle="dropdown"
              style="display: none;">
        <i class="fa fa-chevron-down"></i>
      </button>
      <div class="dropdown-menu @(_isOpen ? "show" : "")"
           style="width: 100%;">
        <h6 class="dropdown-header">
          @Items.Count item(s)
        </h6>
        @foreach (var item in Items)
        {
          <a class="dropdown-item"
           onclick="@(() => TrySelect(item))">
            @ItemTemplate(item)
          </a>
        }
      </div>
    </div>
  </div>
</div>

To be precise, a hidden field to collect via code the unique ID of the selected data item may still be necessary to ensure that once used within an HTML form the type-ahead component can successfully post its content through the normal channel of the browser. However, now the hidden field can be put more naturally outside the boundaries of the type-ahead component, and be under the full control of the client developer.

The type-ahead component defines a template property named ItemTemplate, which is defined as follows:

[Parameter]
RenderFragment<TItem> ItemTemplate { get; set; }

The TItem parameter is defined by the caller. In summary, here’s the markup that sets up a type-ahead component with item templates:

<Typeahead TItem="Country"
           url="/hint/countries"
           name="country"
           onSelectionMade="@ShowSelection">
  <ItemTemplate>
    <span>@context.CountryName</span>&nbsp;
    <b>(@context.ContinentName)</b>
    <small class="pull-right">@context.Capital</small>
  </ItemTemplate>
</Typeahead>

The TItem parameter tells the component it will be handling objects of type Country and receiving hints from the specified URL. Whenever hints based on the entered text are received, they’re rendered in a dynamic dropdown list using the markup in the ItemTemplate section. By design, the item template receives an instance of the current data item and builds up an HTML row. Needless to say, the shape of the dropdown list is now entirely under the control of the page author. And this is a huge step forward (see Figure 5).

The Country Dropdown List
Figure 5 The Country Dropdown List

Wrapping Up

The TypeAhead component is an interesting example of component programming in Blazor. It incorporates the logic to retrieve data over the HTTP protocol, as well as templates and some internal interaction between the elements (the input field and the dropdown list). It then communicates with the outside world via events.

Born as a catchy experiment, Blazor is growing significantly, though the way ahead isn’t completely clear and may well change in the coming months. At this time, the primary goal of the team is to ship support for running Blazor client-side in the browser over WebAssembly.

At the same time, embedding Blazor in ASP.NET Core has many benefits, not the least of which is a much faster application load time. The Blazor components that will be integrated in ASP.NET Core 3.0 will be renamed to Razor Components. It’s just a different name chosen to keep things clear and possibly avoid confusion between what runs in the browser and what runs on the server producing output for the client. At any rate, the component model is expected to stay the same regardless of whether you’re running on the server or the client.


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