Create Templated Components in Blazor

A Blazor application consists of one or more Razpr Components. Typically a component houses its UI in the form of HTML markup and the associated logic. However, at times you want the parent component to specify fragments of the UI so that these fragments can be rendered to produce the final UI of your component. These fragments of UI are called templates. If you ever worked with ASP.NET Web Form's data bound controls such as GridView, ListView, and DataList you are already familiar with the idea of templates and templated controls. Luckily, you can easily create templated components in Blazor so that your component becomes more reusable and customizable.

Now that you some idea about what a templated component is, let's create one in a Blazor Server app.

In the remainder of this article you will create a Razor Component called DataList. The DataList component is used to display a list of Employees and a list of Customers from the Northwind database. When the component displays a list of Emploees, it's UI is as shown below:

And when the component displays a list of Cusotmers, it's UI is like this:

As you can see, the same component is rendering a totally different UI.

To create the DataList component begin by creating a new Blazor Server app.

Then add two new Razor Components namely DataList.razor and DataListConsumer.razor in the Pages folder. 

Then open appsettings.json and specify the database connection string as follows:

"ConnectionStrings": {
  "AppDb": "data source=.;initial catalog=Northwind;
integrated security=true"
}

Then add NuGet package for Microsoft.EntityFrameworkCore.SqlServer so that we can use EF Core for data access.

Next, add three classes that define the EF Core model namely AppDbContext, Employee, and Customer in the Data folder. These classes are shown below:

[Table("Employees")]
public class Employee
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Title { get; set; }
}

[Table("Customers")]
public class Customer
{
    public string CustomerID { get; set; }
    public string CompanyName { get; set; }
    public string ContactName { get; set; }
    public string Country { get; set; }
}

public class AppDbContext:DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> 
options) : base(options)
    {
    }
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Customer> Customers { get; set; }
}

Then open Startup.cs file and add the following code in the ConfigureServices() method.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
    services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
this.Configuration.GetConnectionString("AppDb")));
}

Now open DataList.razor component and the @typeparam directive at the top as shown below:

@typeparam DataItemType 

The @typeparam directive specifies the name of the generic type parameter. This name is used later while defining the templates (it's like the T parameter in C# generics).

Then add a @code block with a set of properties as shown below:

@code {  

    [Parameter]  
    public List<DataItemType> DataItems { get; set; }  

    [Parameter]  
    public RenderFragment HeaderTemplate { get; set; }  
      
    [Parameter]  
    public RenderFragment<DataItemType> ItemTemplate { get; set; } 
      
    [Parameter]  
    public RenderFragment FooterTemplate { get; set; }  
}  

As you can see, the code defines four parameter properties namely DataItems, HeaderTemplate, ItemTemplate, and FooterTemplate. The DataItems property holds a list of data items that are rendered by the control. In our example we specify it to be a List<DataItemType> but it can be any other object as per your requirement. The HeaderTemplate property is a RenderFragment that represents the UI displayed in the header of the DataList. The FooterTemplate is quite similar to HeaderTemplate. The ItemTemplate is RenderFragment<DataItemType>. This way the ItemTemplate can iterate through the data items and render each item in the UI.

Now let's define the skeleton of the DataList control. Add the following markup below the @code block.

<table border="1" cellpadding="10">  
    <thead>  
        <tr>@HeaderTemplate</tr>  
    </thead>  
    <tbody>  
        @foreach (var item in DataItems)  
        {  
            <tr>@ItemTemplate(item)</tr>  
        }  
    </tbody>  
    <tfoot>  
        <tr>@FooterTemplate</tr>  
    </tfoot>  
</table>  

As you can see, there is a table and its three sections - thead, tbody, and tfoot. The HeaderTemplate and FooterTemplate are rendered in the thead and tfoot sections respectively. The data items are rendered by iterating through DataItems and rendering ItemTemplate for each data item.

 This completes DataList component. Let's use it in the DataListConsumer component to display employees and customers.

Open DataListConsumer component and add the following:

@page "/datalistdemo"  
@inject AppDbContext db
@using TemplatedComponentsDemo.Data

Here, we configured the route for the component so that it can be accessed as a "page". Then we injected AppDbContext because we need to retrieve data later in the component.

Now add @code block as shown below.

@code {

    public List<Employee> EmployeeData { get; set; }
    public List<Customer> CustomerData { get; set; }
      
    protected override void OnInitialized()
    {
        EmployeeData = db.Employees.Take(2).ToList();
        CustomerData = db.Customers.Take(3).ToList();
    }  
}  

We defined two properties EmployeeData and CustomerData. These properties are assigned a value in the OnInitialized() left cycle method. The EmployeeData holds a List of Employee objects and the CustomerData holds a List of Customer objects.

We will first display the employees using the DataList component. So, add the following markup in DataListConsumer component.

@if (EmployeeData != null)  
{  
    <DataList DataItems="@EmployeeData" 
              Context="context" 
              DataItemType="Employee">  
        <HeaderTemplate>  
            <td  align="center">
                <h3>List of Employees</h3>
            </td>
        </HeaderTemplate>  
        <ItemTemplate>  
             <table cellpadding="10">
                <tr>
                    <td align="right">Employee ID :</td>
                    <td align="left">@context.EmployeeID</td>
                </tr>
                 <tr>
                    <td align="right">First Name :</td>
                    <td align="left">@context.FirstName</td>
                </tr>
                 <tr>
                    <td align="right">Last Name :</td>
                    <td align="left">@context.LastName</td>
                </tr>
                 <tr>
                    <td align="right">Title :</td>
                    <td align="left">@context.Title</td>
                </tr>
            </table>
        </ItemTemplate>  
        <FooterTemplate>  
            <td  align="center">
                <h3>Total Employees : @EmployeeData.Count</h3>
            </td>  
        </FooterTemplate>  
    </DataList>  
}  

Notice the <DataList> markup. We set three properties in the markup - DataItemType, DataItems, and Context. The DataItemType specifies the type of the data items. In this case we want to display a list of employees and hence DataItemType is Employee. The DataItems property holds the actual data. Here the EmployeeData property contains the employee data. The Context property specifies a parameter name that is used in the child content while rendering the UI. You can specify Context in the ItemTemplate also. Note that specifying DataItemType and Context is optional. If you don't specify DataItemType it will be inferred from the data. If you don't specify the Context, the default parameter name context will be assumed.

Inside the <DataList> we specify the UI for <HeaderTemplate>, <ItemTemplate>, and <FooterTemplate>. Note that these markup element names must match the corresponding property names (HeaderTemplate, ItemTemplate, and FooterTemplate) defined in the DataList component.

Notice how the @context is used to access the Employee properties in the <ItemTemplate>.

Next, add another <DataList> that displays the customer data.

@if (CustomerData != null)  
{  
    <DataList DataItems="@CustomerData">  
        <HeaderTemplate>  
            <th>Customer ID</th>  
            <th>Company Name</th>  
            <th>Contact Name</th> 
            <th>Country</th>
        </HeaderTemplate>  
        <ItemTemplate>  
            <td>@context.CustomerID</td>  
            <td>@context.CompanyName</td>  
            <td>@context.ContactName</td>  
            <td>@context.Country</td>  
        </ItemTemplate>  
        <FooterTemplate>  
            <td colspan="3">
                <h3>Total Customers : 
                    @CustomerData.Count</h3>
            </td>  
        </FooterTemplate>  
    </DataList>  
}

This markup is similar to the earlier one except that it omits the DataItemType and Context properties from the <DataList> element.

Now run the application and navigate to /datalistdemo. If all goes well, you should see the employee listing and customer table as shown in the beginning of this article.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant, trainer, author, and meditation teacher. He has been programming, meditating, and teaching for 25+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Facebook  Twitter  LinkedIn  YouTube

Posted On : 01 September 2021


Tags : ASP.NET ASP.NET Core Data Access .NET Framework C# Visual Studio