Using Razor Components In A Razor Page

In my last article, I looked at some of the new things that ASP.NET Core 3.0 will introduce to Razor Pages developers. One of the features that I touched on was Razor Components. In this article, I take a more detailed look at what they are and how they can be used.

Razor components form part of the Blazor framework. Blazor is a single page application development framework whose key feature is that it enables you to write the entire application in C# and have it execute in the browser. To do this, a new .NET runtime is needed that works with WebAssembly. Blazor began life as an experimental framework. As part of its evolution, a server-side model was introduced where the C# code executes on the web server, and a SignalR connection is used to make updates to the application in the browser in real time. This model was officially incorporated as part of .NET Core 3.0 in Preview 2, and was initially named Razor Components. In Preview 4, the development model was renamed to Server-side Blazor. The term Razor Component now refers to a specific building block within the Blazor framework rather than a development framework itself.

Within Blazor, Razor components can perform many roles. They can represent a specific piece of UI, equivalent to a Razor Pages partial, view component or a tag helper. Or they can represent a layout. Or they can represent an entire page. When used in a Razor Page (or indeed an MVC view), they will take the place of a partial, a view component or a custom tag helper.

Razor components are created in files with a .razor extension. They can also be created in .cshtml files, but that way lies potential confusion if you ask me. This is enabled mainly to aid backward compatibility with older previews. The .razor file contains a mixture of HTML and C#, embedded in the HTML using the familiar Razor syntax.

A Pager Component

The example that follows shows how to create a Razor component that generates paging links and then incorporate it into a Razor page. This has been developed using Visual Studio 2019 16.3.0 Preview 1.0, and Preview 7 of .NET Core 3.0.

  1. The first step is to make Blazor available to the application. This is done in the ConfigureServices method:
    services.AddServerSideBlazor();
  2. Next, add a folder named Components to the project. Within the folder, add a new file named _Imports.razor. You can use the Razor Component template option to do this. Or you can add a text file and change the extension.
  3. Replace any existing content in the _Imports.razor file with the following line of code:
     @using Microsoft.AspNetCore.Components

    This file works in a similar way to how _ViewImports.cshtml works within a Razor Pages application except that it only affects Blazor artefacts. It provides a way to bring namespaces into scope within Razor components placed in the same folder as the file, or any sub folders.

  4. Add a new file to the Components folder named Pager.razor. Use the Razor Component option available from Add new item:

    Add Razor Component

  5. Replace any existing content with the following code:
    <ul class="pagination">
        @for(var i = 1; i <= totalPages; i++)
        {
            <li class="page-item @(i == pageNumber ? "active" : "")">
                <a href="@linkUrl/@i" class="page-link">@i</a>
            </li>
        }
    </ul>
    @code {
        [Parameter] int pageNumber { get; set; }
        [Parameter] int totalRecords { get; set; }
        [Parameter] int pageSize { get; set; } = 20;
        [Parameter] string linkUrl { get; set;  }
        int totalPages => (int)Math.Ceiling((decimal)totalRecords / pageSize);
    }

    The code at the top of the file is familiar to anyone who has worked with Razor. It combines HTML with C# to render an unordered list containing list items for each paging link. The markup makes use of Bootstrap 4 styling to render buttons.

    The @code block is equivalent to the @functions block that you might see in MVC views and Razor Pages. In fact, you can replace @code with @functions in a .razor file, although you can't use @code in a .cshtml file - at least not in .NET Core 3.0 Preview 7. This is where you place blocks of C# code, including property declarations and local methods.

    The [Parameter] attribute is used to expose private properties as parameters to callers so that you can pass arbitrary values in to the component. In this case, you want to allow the caller to be able to pass in the current page (pageNumber), the total number of records, the number of records per page (pageSize) and the URL that the link should point to. The total number of paging links is calculated from the number of records and the page size.

  6. The RenderComponentAsync<T> method is used to include the component in a Razor page, with parameter values passed as an object:
    @(await Html.RenderComponentAsync<Pager>( new { pageNumber = Model.PageNumber, totalRecords = 150, pageSize = 20, linkUrl = Url.PageLink("Index")}))
    Razor Component

You don't have to place your properties and methods in an @code block. You can create a class that derives from ComponentBase and then have the .razor file inherit from that. Here's how that translates to the Pager component above.

Create a class named PagerComponent with the following content:

using Microsoft.AspNetCore.Components;

public class PagerComponent : ComponentBase
{
    [Parameter] public int PageNumber { get; set; }
    [Parameter] public int TotalRecords { get; set; }
    [Parameter] public int PageSize { get; set; } = 20;
    [Parameter] public string LinkUrl { get; set; }
    public int TotalPages => (int)Math.Ceiling((decimal)TotalRecords / PageSize);
}

Then alter the content of the Pager.razor file as follows:

@inherits PagerComponent
<ul class="pagination">
    @for(var i = 1; i <= TotalPages; i++)
    {
        <li class="page-item @(i == PageNumber ? "active" : "")">
            <a href="@LinkUrl/@i" class="page-link">@i</a>
        </li>
    }
</ul>

If you re-run the application, the component should render as before. There is no need to change the casing of the parameter names in the object that you pass to the RenderComponentAsync method to match the casing of the new class public properties. Blazor does a case-insensitive match when binding parameter values to properties.

Summary

Razor components can easily be used within a Razor Pages application. UI and processing code can be partitioned within the same file, or separated out into different files, which can help with unit testing.