Advanced Razor Components features in Blazor

Advanced Razor Components features in Blazor

Advanced Razor Component features can be built by understanding the concepts of Custom Binding, Template Components, Generic Template Components & Cascading Parameters. Let’s understand each of them.

Custom Binding between Parent & Child components – “chained bind”

Custom Binding is a Two-Way Data Binding between Parent and Child Razor Components. It is a binding of a child component’s property to a property in it’s parent. This scenario is also called a chained bind because multiple levels of binding occurs simultaneously. Keep in mind that In Blazor Data Binding:

  • New parameter values flow down the hierarchy i.e. from parent to child.
  • Change notifications flow up the hierarchy i.e. from child to parent.

Let us create an example for it. So, inside the Pages folder of your app, create 2 razor components called:

  • 1. Parent.razor
  • 2. Child.razor

Add the following code to the Parent.razor.

@page "/Parent"
<Child @bind-MyPhrase="wwe" />

@code {
    private string wwe = "Bill Goldberg is stronger than Brock Lesnar";
}

I provided @bind-MyPhrase attribute for the Child component. This will bind the MyPhrase component property in the child component with the value of string variable wwe.

In my last tutorial I covered Razor Components from start, kindly check it if you haven’t done till now – How to use Razor Components in Blazor

Note : Component parameters permit binding of properties and fields of a parent component with @bind-{PROPERTY OR FIELD} syntax. This way I will be able to transfer the text from parent to child component.

Now in the Child.razor. Add the following code.

<h3 class="bg-info text-white">@MyPhrase</h3>

@code {
    [Parameter]
    public string MyPhrase { get; set; }

    [Parameter]
    public EventCallback<string> MyPhraseChanged { get; set; }
}

The property called MyPhrase is a Component Parameter that will receive the value of text “Bill Goldberg is stronger than Brock Lesnar” transferred from the parent component.

There should also be defined a callback method of type EventCallback, and it should be named same as the component parameter name followed by the “Changed” suffix. So, by this convention I have to name it as MyPhraseChanged. The TValue should be the type of Component Parameter which in my case is string.

Now 2-Way data binding is completed between parent and child components. First let us see how parent sends the text to the child. Run your app and go to the url – https://localhost:44366/Parent. You will see the text that is send by the parent i.e. “Bill Goldberg is stronger than Brock Lesnar”. Check below image to confirm.

blazor custom binding

Now I will show how the data binding works from child component back to parent component. So, in the child component called Child.razor I add a button which will invoke the callback method called MyPhraseChanged on it’s click event. The button and it’s click event codes are shown in highlighted manner below:

<h3 class="bg-info text-white">@MyPhrase</h3>

<button class="btn btn-warning" @onclick="UpdatePhrase">Update Phrase</button>

@code {
    [Parameter]
    public string MyPhrase { get; set; } 

    [Parameter]
    public EventCallback<string> MyPhraseChanged { get; set; }

    private async Task UpdatePhrase()
    {
        await MyPhraseChanged.InvokeAsync("Brock Lesnar is stronger than Bill Goldberg");
    }
}

So, on the button click the changed text Brock Lesnar is stronger than Bill Goldberg is transferred to the parent. The “wwe” property defined on the parent will receive this updated value, and you will see the text gets updated to Brock Lesnar is stronger than Bill Goldberg on the browser.

In the below video I have demonstrated this feature.

blazor chained bind

You can use the chained bind approach for any number of nested components.

Blazor Template Components – “Code Reusability”

A Template Component in Blazor is used to customize the UI’s feel and look in a reusable way. Template Components accepts UI templates that will be used while rendering. You will love the way it works so stay tuned.

In order to make a razor component as template component, you should define in it, one or more properties of type RenderFragment or RenderFragment. These properties should have [Parameter] attribute. See below code where I have defined 2 such properties.

[Parameter]
public RenderFragment P1 { get; set; }

[Parameter]
public RenderFragment<TValue> P2 { get; set; }

The @typeparam directive is defined on the top of the Template component and it defines the type parameter i.e. TValue of P2 property.

Let us create a razor component called TableTemplate.razor inside the Pages folder of the app. Next, add the following code to it:

@typeparam RowType
<table class="table table-sm table-bordered table-striped">
    @if (Header != null)
    {
        <thead>@Header</thead>
    }
    <tbody>
        @foreach (RowType item in RowData)
        {
            <tr>@RowTemplate(item)</tr>
        }
    </tbody>
</table>

@code {
    [Parameter]
    public RenderFragment Header { get; set; }

    [Parameter]
    public RenderFragment<RowType> RowTemplate { get; set; }

    [Parameter]
    public IEnumerable<RowType> RowData { get; set; }
}

Since I have 2 properties of type RenderFragment (“Header”, “RowTemplate”) therefore this razor component is a Template Components.

I have defined @typeparam RowType on top of this component. So, when this component will be called from another component, like from a parent component, then at that time the RowType will be defined. Say of example, it can be defined as a Dog class type, Cat class type, Person class type, Student class type etc.

The property called RowData is a Component parameter and it’s value will also be provided by the parent component. I have defined it as IEnumerable so I can loop through it’s individual values and create a HTML table containing these values. This is exactly what I did in the below foreach loop.

@foreach (RowType item in RowData)
{
    <tr>@RowTemplate(item)</tr>
}

The most important thing is the razor expression – @RowTemplate(item). It tells to apply the UI template, which is passed to the RowTemplate, to the current value of the RowData (i.e. item variable). Being on the foreach loop it applies to all the values contained in the RowData property.

Related tutorial – Blazor Events and Data Bindings

I hope it started making sense to you. I will next describe the parent component which will provide all these values, and then the working of every part would become totally clear to you.

So, create a new Razor Component called ExampleT.razor and defined it’s code as given below:

@page "/ExampleT"

<TableTemplate RowType="Person" RowData="persons">
    <Header>
        <tr><th>Name</th><th>City</th></tr>
    </Header>
    <RowTemplate Context="p">
        <td>@p.Name</td>
        <td>@p.City</td>
    </RowTemplate>
</TableTemplate>

@code {
    private List<Person> persons = new List<Person>{
        new Person { Name = "Jack", City = "New York" },
        new Person { Name = "Sarah", City = "Boston" },
        new Person { Name = "Chan", City = "Hong Kong" }
    };

    private class Person
    {
        public string Name { get; set; }
        public string City { get; set; }
    }
}

In this razor component I defined a Person class and a “persons” variable of type List. I have added 3 persons having name and city to this variable. Next, see I called the TableTemplate and provided it with 2 attributes:

  • 1. RowData attribute with the value of “persons” variable. The IEnumerable RowData property defined in the TableTemplate.razor will receive it’s value.
  • 2. RowType attribute as “Person” class type. This will specify the @typeparam RowType in the TableTemplate that RowType will be Person class type.

Next, there are “header” and “RowTemplate” nodes which provides the UI content to the 2 properties which are:

[Parameter]
public RenderFragment Header { get; set; }

[Parameter]
public RenderFragment<RowType> RowTemplate { get; set; }

So, “Header” property will receive the value inside the Header node which is:

<tr><th>Name</th><th>City</th></tr>

It does nothing special except for showing it inside thead element:

<thead>@Header</thead>

While the “RowTemplate” property will receive the value inside the RowTemplate node. The RowTemplate nodes value will be repeated for every value contained by the property “RowData” so table rows will be formed for each of the 3 persons.

So, what is Context attribute? The RenderFragment RowTemplate property is called for every value of the RowData variable contained by “item” variable – @RowTemplate(item).

With the Context attribute I am specifying the “item” value only – like here Context=”p”, p is just a variable and you can name it anything like a, b, aa, abc, etc.

Now see the RowTemplate node’s code:

<RowTemplate Context="p">
    <td>@p.Name</td>
    <td>@p.City</td>
</RowTemplate>

With the variable “p” I can go to individual values of the person class like “Name” and “City” in my case and get the value of every person.

So, in this way a whole of the HTML table is formed. You can now run the app and go to the url – https://localhost:44366/ExampleT, where you will see all the 3 persons in html table format. See below image:

Blazor Template Components

Reusability by Template Components

Let us see the reusability provided by Template Components. So, create a new razor component and name it Dogs.razor. Add the following code to it:

@page "/Dogs"

<TableTemplate RowType="Dog" RowData="dogs">
    <Header>
        <tr><th>Name</th><th>City</th></tr>
    </Header>
    <RowTemplate Context="p">
        <td>@p.Breed</td>
        <td>@p.Origin</td>
    </RowTemplate>
</TableTemplate>

@code {
    private List<Dog> dogs = new List<Dog>{
        new Dog { Breed = "German Shepherd", Origin = "Germany" },
        new Dog { Breed = "Bulldog ", Origin = "United Kingdom" },
        new Dog { Breed = "Rottweiler", Origin = "Germany" }
    };

    private class Dog
    {
        public string Breed { get; set; }
        public string Origin { get; set; }
    }
}

In this razor component I am providing Dogs breed data to the TableTemplate. You can see I did not do even a single code line change in the TableTemplate and everything works very fine. This is a great reusability of code and you are going to love this approach.

You should also consider reading my article called Creating First Application in Blazor from scratch where I covered every Blazor file and folder along with their working.

Run the app go to the url – https://localhost:44366/Dog to see all the dog displayed in html table. See below image:

Blazor Template Components reusability

Cascading Values and Parameters

Suppose there are 3 Razor components – ‘One.razor’, ‘Two.razor’ & ‘Three.razor’. We had done nesting of these components that is first component calls the second and the second calls the third.

Now suppose One.razor has to send some value to the other 2 razor components. One way to do it is through Component Parameters, here each component in the chain receive the data and pass it on to the next one. But this approach is error-prone and requires every component to participate in the process.

Let us send some data from One.razor to other components using Component Parameter. The data which I am transferring is a text called “Hello” which is contained by a variable ‘MyMessage’, and it is defined in the One.razor component.

The codes of these components are given below.

One.razor
@page "/One"

<div class="p-5 bg-success">
    <Two MyMessage="@MyMessage"></Two>
</div>

@code {
    public string MyMessage = "Hello";
}
Two.razor
<div class="p-5 bg-secondary">
    <h3 class="text-white">@MyMessage</h3>
    <Three MyMessage="@MyMessage"></Three>
</div>

@code {
    [Parameter]
    public string MyMessage { get; set; }
}
Three.razor
<div class="p-5 bg-warning">
    <h3 class="text-white">@MyMessage</h3>
</div>

@code {
    [Parameter]
    public string MyMessage { get; set; }
}

To make this visually clear through Bootstrap class the first component is given background color of green, the second one is given grey background color and the last one yellow color.

Run your app and go to the URL – https://localhost:44366/One and you can see there are 2 Hello text displayed. First in Two.razor and other in Three.razor.

nested razor components

Although the work is done but there is a nicer approach to do this by Cascading Values and Parameters.

Blazor comes with a built-in razor component called CascadingValue. This component has an attribute called Value which can be provided with a data that needs to be transferred.

You will need to wrap a child component inside it and every successive component in the nesting tree will be able to get the transferred data. I will do the wrapping of the second razor component and that will do the work. So, the necessary changes to be made to One.razor is shown in the below code.

@page "/One"

<div class="p-5 bg-success">
    <CascadingValue Value="@MyMessage">
        <Two></Two>
    </CascadingValue>
</div>

@code {
    public string MyMessage = "Hello";
}  

The Hello value is now available to both of the nested razor components. Accessing this value is done by Cascading Parameters which will be defined in the Two.razor and Three.razor.

Cascading Parameters have the attribute [CascadingParameter] and their type should be same like the value that is transferred. In my case they will be of string type. So update the Two.razor code as shown below:

<div class="p-5 bg-secondary">
    <h3 class="text-white">@TransferredValue</h3>
    <Three></Three>
</div>

@code {
    [CascadingParameter]
    public string TransferredValue { get; set; }
}

Similarly update the Three.razor code as given below:

<div class="p-5 bg-warning">
    <h3 class="text-white">@TransferredValue</h3>
</div>

@code {
    [CascadingParameter]
    public string TransferredValue { get; set; }
}

This is a very clean approach.

Multiple Cascading Parameters

To transfer multiple values then nest multiple CascadingValue components and provide a unique Name string to each of the CascadingValue components. In the below code I am transferring 2 values from “One.razor”:

@page "/One"

<div class="p-5 bg-success">
    <CascadingValue Name="Cascade1" Value="@MyMessage">
        <CascadingValue Name="Cascade2" Value="@MyNumber">
            <Two></Two>
        </CascadingValue>
    </CascadingValue>
</div>

@code {
    public string MyMessage = "Hello";
    public int MyNumber = 99;
}

In the descendant components (i.e. here Two.razor and Three.razor), the cascading parameters receive their values by the name of the CascadingValue components. So, the code of Two.razor becomes:

<div class="p-5 bg-secondary">
    <h3 class="text-white">@T1</h3>
    <h3 class="text-white">@T2</h3>
    <Three></Three>
</div>

@code {
    [CascadingParameter(Name ="Cascade1")]
    public string T1 { get; set; }

    [CascadingParameter(Name = "Cascade2")]
    public int T2 { get; set; }
}

Similarly, the code of Three.razor becomes:

<div class="p-5 bg-warning">
    <h3 class="text-white">@T1</h3>
    <h3 class="text-white">@T2</h3>
</div>

@code {
    [CascadingParameter(Name = "Cascade1")]
    public string T1 { get; set; }

    [CascadingParameter(Name = "Cascade2")]
    public int T2 { get; set; }
}

Run the app and you will see what is shown by the below image:

Multiple Cascading Parameters

Handling Errors in Blazor

In this section I will explain how to handle different types of Errors in Blazor so that users can receive proper error messages if something goes wrong. Errors can be defined by 2 types, which are:

  • 1. Connection Errors
  • 2. Application Errors

Connection Errors

Blazor relies on a persistent HTTP Connection between Browser and Server. If this connection gets broken down then it automatically displays a modal error message called Attempting to reconnect to the server: 1 of 8, and prevents the user from interacting with components.

So, first create a new Razor Component called ExampleE.razor inside the Pages folder with the following code:

@page "/ExampleE"

<h1 class="bg-info text-white">Blazor Errors</h1>

@code {

}

Now run the app and a new browser window will open where the default app URL gets opened. In this window navigate to the URL – https://localhost:44366/ExampleE, and you can see the newly created razor component gets displayed.

Now in order to break the connection, open a new browser window, and in it open this same URL. Next, stop the app by clicking the stop button in Visual Studio. Visual Studio will close the browser window that was opened by it when you ran the app.

The browser window you opened the second time will remain open and Blazor will show you connection error message – Attempting to reconnect to the server: 1 of 8. After making attempts to reconnect it fails and displayed another message Reconnection failed. Try reloading the page if you’re unable to reconnect.. See the below screenshot of this error message.

blazor connection error
Customize Blazor Error Messages

Now I will show you how to customize this error message by using Bootstrap CSS to a very friendly look. I will also add a few buttons to further interact with the browser.

In the ExampleE.razor add the following highlighted code of the style and div elements which is given below:

@page "/ExampleE"

<style>
    #components-reconnect-modal {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 100;
        opacity: 0.8;
    }

    .components-reconnect-hide, .components-reconnect-show > .failed, .components-reconnect-show > .rejected, .components-reconnect-failed> .reconnect, .components-reconnect-failed > .rejected, .components-reconnect-rejected > .reconnect, .components-reconnect-rejected > .failed {
        display: none;
    }

    .components-reconnect-show, .components-reconnect-show > .reconnect, .components-reconnect-failed > .failed, .components-reconnect-rejected> .rejected {
        display: block;
    }
</style>

<div id="components-reconnect-modal" class="h3 bg-danger text-white text-center my-1 p-1 components-reconnect-hide">
    Blazor Connection Lost
    <div class="reconnect">
        Trying to reconnect...
    </div>
    <div class="failed">
        Reconnection Failed.
        <button class="btn btn-light" onclick="window.Blazor.reconnect()">
            Reconnect
        </button>
    </div>
    <div class="rejected">
        Reconnection Rejected.
        <button class="btn btn-light" onclick="location.reload()">
            Reload
        </button>
    </div>
</div>

<h1 class="bg-info text-white">Blazor Errors</h1>

@code {

}

Explanation : Whenever there is an connection break then Blazor will locate an element having id as components-reconnect-modal. It is the reason why I added this div element on the top:

<div id="components-reconnect-modal" class="h3 bg-danger text-white text-center my-1 p-1 components-reconnect-hide">

Inside this element Blazor adds one of the four Connection Error CSS classes given in the table below.

Name Description
componentsreconnect-show This class is added when Blazor is attempting a reconnection to the server.
componentsreconnect-hide This class is added if the connection is re-established.
componentsreconnect-failed This class is added if the reconnection fails.
componentsreconnect-rejected This class is added if the connection is re-established but user’s connection state is lost. Possible when server is restarted.

Based on these CSS I have added 4 div elements under the main div:

<div class="reconnect">
    Trying to reconnect...
</div>
<div class="failed">
    Reconnection Failed.
    <button class="btn btn-light" onclick="window.Blazor.reconnect()">
        Reconnect
    </button>
</div>
<div class="rejected">
    Reconnection Rejected.
    <button class="btn btn-light" onclick="location.reload()">
        Reload
    </button>
</div>

I have also provided 2 buttons (Reconnect and Reload) when the reconnection failed and when reconnection is rejected. The Reconnect button calls JavaScript method window.Blazor.reconnect() when it is clicked and this will try reconnection to the server.

While the Reload button calls another JavaScript method location.reload() when clicked. This will reload the whole page.

Testing

Run the app and open the URL- https://localhost:44366/ExampleE in a new window. Now stop the app in VS and you will see the message “Blazor Connection Lost Trying to reconnect…” on the browser. Check below screenshot.

Blazor Connection Error componentsreconnect-show

After a few seconds, you will see the message that indicates that reconnection has failed with a Reconnect button. Check the below screenshot.

Blazor Connection Error componentsreconnect-failed

To see the reconnection rejected status. Do the same procedure till you get “Blazor Connection Lost Trying to reconnect…”. Then start the app in Visual Studio, do this before you see “reconnection has failed” message.

After a few second the same window will show “Blazor Connection Lost Reconnection Rejected.” With a Reload button. Check the below screenshot:

Blazor Connection Error componentsreconnect-rejected

Application Errors

When an application error happens then Blazor looks for an element whose id is blazor-error-ui and sets its CSS display property to block. Note a reload is needed after an application error else the app would be unresponsive.

So, add a button which will cause an application error. Also add a div with Id “blazor-error-ui” as shown below:

@page "/ExampleE"

<style>
    #components-reconnect-modal {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 100;
        opacity: 0.8;
    }

    .components-reconnect-hide, .components-reconnect-show > .failed, .components-reconnect-show > .rejected, .components-reconnect-failed> .reconnect, .components-reconnect-failed > .rejected, .components-reconnect-rejected > .reconnect, .components-reconnect-rejected > .failed {
        display: none;
    }

    .components-reconnect-show, .components-reconnect-show > .reconnect, .components-reconnect-failed > .failed, .components-reconnect-rejected> .rejected {
        display: block;
    }
</style>

<div id="components-reconnect-modal" class="h3 bg-danger text-white text-center my-1 p-1 components-reconnect-hide">
    Blazor Connection Lost
    <div class="reconnect">
        Trying to reconnect...
    </div>
    <div class="failed">
        Reconnection Failed.
        <button class="btn btn-light" onclick="window.Blazor.reconnect()">
            Reconnect
        </button>
    </div>
    <div class="rejected">
        Reconnection Rejected.
        <button class="btn btn-light" onclick="location.reload()">
            Reload
        </button>
    </div>
</div>

<div id="blazor-error-ui" class="text-center bg-warning text-white p-1" style="display:none">
    An error has occurred. Kindly Reload.
    <button class="btn btn-sm btn-primary" onclick="location.reload()">
        Reload
    </button>
</div>

<h1 class="bg-info text-white">Blazor Errors</h1>

<button class="btn btn-danger" @onclick="@(() => throw new Exception())">
    Error
</button>

@code {

}

Now run the app and go to the URL – https://localhost:44366/ExampleE. Click the error button to throw an exception. You will see an error message in yellow background with a reload button. Check the below screenshot.

blazor Application Errors

You can download the source codes:

Download

Conclusion
In this tutorial I covered some very advanced topics on razor components. I tried to explain them with easy language and hope you got my point. Use these topics to create better Blazor App. Please share this tutorial so that others can also learn them.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate

Leave a Reply

Your email address will not be published. Required fields are marked *