CHAPTER 4
So far, we’ve covered the basics of working with components in Blazor, but we have yet to explore other features that will allow us to use components and add additional features to a Blazor application. That’s what this chapter is all about.
One of the great things about working with frameworks like Blazor is that there’s the possibility to show or not show parts of the UI based on specific conditions.
On the Weather forecast page, let’s add a new feature for each row, where an additional section with extra information can be shown for each data row when an arrow is clicked.

Figure 4-a: Weather Forecast with Additional Details (Which We Have Yet to Create)
So, to be able to make this work, we need to do a few things. Let’s start by exploring the current code that displays row data. This is available within the FetchData.razor page, which resides in the Pages folder. Let’s open this file using Solution Explorer.
Code Listing 4-a: FetchData.razor
@page "/fetchdata" @using BlazorApp.Data @inject WeatherForecastService ForecastService <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } } |
From the preceding listing, the HTML markup responsible for rendering each row of data has been highlighted in bold.
To add the additional section, the first thing we need to do is refactor this code into a separate component. We are going to call it ForecastRow.razor and place it into the Components folder.
So, using Solution Explorer, right-click on the Components folder within the BlazorApp project, click Add, and then click the Razor Component option. Then, name the component ForecastRow.razor and click Add.
Now, open the ForecastRow.razor file and add the following code.
Code Listing 4-b: ForecastRow.razor
@inherits ForecastRowBase <tr> <td>@Forecast.Date.ToShortDateString()</td> <td>@Forecast.TemperatureC</td> <td>@Forecast.TemperatureF</td> <td>@Forecast.Summary</td> <div @onclick="e => Forecast.ShowExtras = !Forecast.ShowExtras"> <IconButton aria-label="delete" Style="margin: 8px; " Size="@IconButtonSize.Small"> <ArrowDownwardIcon Style="font-size: inherit; " /> </IconButton> </div> </tr> @if (Forecast.ShowExtras) { <tr> <td colspan="5"> Extra Information </td> </tr> } |
If you get a warning that “Element ‘div’ cannot be nested inside element ‘tr,’” you can avoid this warning by placing the <div> inside a <td> cell.
The first thing to notice is that the ForecastRow component inherits from the ForecastRowBase class.
This means that the code-behind for the ForecastRow.razor file will reside in a file we have yet to create called ForecastRow.razor.cs, which will contain the ForecastRowBase class. We’ll do this in a bit.
The next thing to notice is that we’ve taken each row item and changed the references from forecast to Forecast. This might seem like a minor change, but it is quite significant.
By making this change, we are saying that the ForecastRow component is no longer getting the forecast data through the forecast object, which is an instance of the WeatherForecast class declared within the WeatherForecast.cs file under the Data folder.
Instead, the ForecastRow component is getting the forecast data through the Forecast property. It will be of type WeatherForecast, and is going to be part of the ForecastRowBase class that the ForecastRow component inherits from. We’ll come back to this point shortly.
Next, we also notice that we’ve added a div that contains an IconButton, which is the arrow highlighted in the preceding figure.
Just like we did with the TestButton component, the onclick event has been added to the div, which is a real HTML element, and not to the IconButton.
The onclick event contains an inline C# lambda operator, which is described as follows.
e => Forecast.ShowExtras = !Forecast.ShowExtras
This toggles (shows or hides) the additional information section, which comes after the @if (Forecast.ShowExtras) instruction.
If the value of the Forecast.ShowExtras property is set to true, the panel is shown. On the contrary, if the value of Forecast.ShowExtras is set to false, the panel is hidden.
Notice as well that the panel spans the length of the five columns of the data row, which is achieved with the <td colspan="5"> instruction.
Now that we’ve explored the ForecastRow component, let’s create the ForecastRow.razor.cs file, which is the code-behind file for the component.
So, using Solution Explorer, click on the Components folder, right-click, click Add, click New Item, and then select the Class item from the list. Let’s give it the name ForecastRow.razor.cs, and then click Add.
Once created, add the following code to ForecastRow.razor.cs.
Code Listing 4-c: ForecastRow.razor.cs
using BlazorApp.Data; using Microsoft.AspNetCore.Components; namespace BlazorApp.Components { public class ForecastRowBase: ComponentBase { [Parameter] public WeatherForecast Forecast { get; set; } } } |
There are two things to notice. The first is that we’ve added a reference to the BlazorApp.Data namespace. This is because the Forecast property is of type WeatherForecast.
The second thing to notice is that the Forecast property is decorated with the Parameter attribute. This means that an instance of WeatherForecast will be passed from the parent to the child component, ForecastRow.
To invoke the ForecastRow component, we need to modify the FetchData.razor file. We’ll do this shortly.
But before we do that, there’s one crucial piece to this conditional rendering puzzle we still need to complete, and that is to add the ShowExtras property to the WeatherForecast class.
So, using Solution Explorer, open the WeatherForecast.cs file that resides in the Data folder, and let’s add the ShowExtras property.
The updated code for the WeatherForecast class follows. The change is highlighted in bold.
Code Listing 4-d: Updated WeatherForecast.cs
using System; namespace BlazorApp.Data { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; }
public bool ShowExtras { get; set; } } } |
Let’s invoke the ForecastRow component we’ve just created. Using Solution Explorer, navigate to the Pages folder and open the FetchData.razor file. Once the file is open, select the following code.
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
Replace this code with <ForecastRow Forecast="forecast" />.
After making this change, the complete code for the FetchData.razor page should be as follows.
Code Listing 4-e: Updated FetchData.razor
@page "/fetchdata" @using BlazorApp.Data @inject WeatherForecastService ForecastService <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading…</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <ForecastRow Forecast="forecast" /> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } } |
If we now run the application by pressing Ctrl+F5, we will see the following when navigating to the Weather forecast page, which we can do by clicking the FETCH DATA option on the app’s menu.

Figure 4-b: Updated Weather forecast Page
If you click on any of the arrows, the extras panel will appear, and if you click again on the same arrow, the panel will hide.
At this stage, we have added the basic functionality of the panel, which is part of the ForecastRow component.
However, we need to add some extra features to this component, such as the ability to display wind and humidity data when the panel is shown, for each row of data. That’s what we’ll do next.
To be able to complete the functionality of the ForecastRow component, we inject an object that will contain wind and humidity information for each row.
So, let’s open the ForecastRow.razor.cs file that resides in the Components folder using Solution Explorer. Then we’ll make some changes to the existing code, which are highlighted in bold in the following listing.
Code Listing 4-f: Updated Forecast.razor.cs
using BlazorApp.Data; using Microsoft.AspNetCore.Components; using System.Threading.Tasks; namespace BlazorApp.Components { public class ForecastRowBase: ComponentBase { [Parameter] public WeatherForecast Forecast { get; set; } protected WeatherExtras Extras { get; set; } [Inject] public WeatherExtrasService ExtrasService { get; set; } protected async override Task OnInitializedAsync() { Extras = await ExtrasService.GetExtrasAsync(Forecast); } } } |
The first thing to notice is that we’ve added to the Forecast.razor.cs code a reference to the System.Threading.Tasks namespace. This is because the OnInitializedAsync life cycle event returns a Task object.
Next, we’ve added an Extras property of type WeatherExtras (which is a class we have not created yet). The Extras property will contain the wind and humidity information.
Then, we have the ExtrasService property (which has not been created yet) injected into the ForecastRowBase class. This is why the property is decorated with the Inject attribute.
ExtrasService will be used to retrieve the additional weather information that will be assigned to the Extras property.
The retrieval of the information will be done on the OnInitializedAsync event, which is a method that overrides the base method with the same name that gets inherited from the ComponentBase class. This is one of the various component life cycle events.
Within OnInitializedAsync, the GetExtrasAsync method from the WeatherExtrasService instance is invoked, which returns a WeatherExtras type object.
The current weather forecast (the Forecast object, which contains the data row details) is passed as a parameter to the GetExtrasAsync method.
Now, let’s create the WeatherExtras class. To do that, let’s create a new file called WeatherExtras.cs under the Data folder.
Using Solution Explorer, click on the Data folder, right-click, click Add, click New Item, and select Class from the items list. Name the file WeatherExtras.cs, and then click Add.
Once the file has been created, open it, and let’s add the following code to it.
Code Listing 4-g: WeatherExtras.cs
namespace BlazorApp.Data { public class WeatherExtras { public WeatherForecast Forecast { get; set; } public int Humidity { get; set; } public string Wind { get; set; } } } |
As you can see, there’s nothing too complicated about this code. We are simply declaring the Forecast, Humidity, and Wind properties.
This class represents the model for the extra weather data—nothing else. That was easy.
Now let’s shift our attention to the service (WeatherExtrasService) that will be responsible for retrieving the additional weather data and assigning it to the model.
Using Solution Explorer, right-click the Data folder, then click Add, and after that click New Item and select Class from the items list. Name the file WeatherExtrasService.cs, and then click Add.
Once the file has been created, open it, and let’s add the following code to it.
Code Listing 4-h: WeatherExtrasService.cs
using System; using System.Threading.Tasks; namespace BlazorApp.Data { public class WeatherExtrasService { private static readonly string[] WindTypes = new[] { "Strong", "Heavy", "Mild", "Chilly", "No wind", "Hurricane", "Bursts", "Gentle breeze" }; public Task<WeatherExtras> GetExtrasAsync(WeatherForecast forecast) { var rng = new Random(); return Task.FromResult(new WeatherExtras { Forecast = forecast, Humidity = rng.Next(-20, 55), Wind = WindTypes[rng.Next(WindTypes.Length)] }); } } } |
The code for this service is quite similar to the code of the WeatherForecastService.cs file that came out of the box when the project was created.
The GetExtrasAsync method simply creates a new instance of the WeatherExtras class and assigns random data to the Humidity and Wind properties of the object created, which is then returned by the method.
The value of the Humidity property is assigned based on random numbers. The value of the Wind property is assigned based on a random choice of any of the items contained within the WindTypes array.
In a real-world application, a service like this would be responsible for retrieving the data from the back end, such as a SQL Server instance. In our case, we are just using randomly generated data.
Before the service can be used, it needs to be registered in the ConfigureServices method of the Startup.cs file that resides in the project’s root folder. So, let’s do that.
Using Solution Explorer, open the Startup.cs file and locate the ConfigureServices method. Then add the following line.
services.AddSingleton<WeatherExtrasService>();
The updated ConfigureServices method should look as follows.
Code Listing 4-i: Registering WeatherExtrasService (ConfigureServices Method of Startup.cs)
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); services.AddSingleton<WeatherExtrasService>(); services.TryAddMaterialServices(new MaterialConfigBuilder().Build()); } |
Before we can try this new functionality, I’d like to make a small cosmetic update to the HTML markup of the ForecastRow component so that the Humidity and Wind values can be visible.
In the listing that follows, I’ve highlighted the changes in bold.
Code Listing 4-j: Updated ForecastRow.razor File
@inherits ForecastRowBase <tr> <td>@Forecast.Date.ToShortDateString()</td> <td>@Forecast.TemperatureC</td> <td>@Forecast.TemperatureF</td> <td>@Forecast.Summary</td> <div @onclick="e => Forecast.ShowExtras = !Forecast.ShowExtras"> <IconButton aria-label="delete" Style="margin: 8px; " Size="@IconButtonSize.Small"> <ArrowDownwardIcon Style="font-size: inherit; " /> </IconButton> </div> </tr> @if (Forecast.ShowExtras) { <tr> <td> <strong>Humidity:</strong> @Extras.Humidity% </td> <td> <strong>Wind:</strong> @Extras.Wind </td> </tr> } |
All I’ve done is render the values of the Humidity and Wind properties from the Extras instance.
If we now run the application by pressing Ctrl+F5, we’ll be able to see the changes when navigating to the Weather forecast page. Just click on a couple of arrows on different rows to see the additional information.

Figure 4-c: Updated Weather Forecast with Extras
Because the data is generated randomly, don’t expect the values to make much sense. The important takeaway is how to compose and use components within a Blazor application.
We just saw with the WeatherExtrasService class that by using the OnInitializedAsync method, we were able to retrieve the data to assign to the Extras property of the ForecastRowBase instance that the ForecastRow component inherits from.
Of all the life cycle methods Blazor components can use, you’ll probably be using OnInitializedAsync the most. Nevertheless, there are other life cycle methods available. Let’s have a look at some of them.
There’s the OnParametersSetAsync method (the synchronous version of this method is called OnParametersSet), which is triggered each time a component receives a new parameter from its parent.
If you’d like to implement this method in your component, it would have to be as follows.
Code Listing 4-k: Implementing OnParametersSetAsync
protected override Task OnParametersSetAsync() { return base.OnParametersSetAsync(); } |
This method could be used, for instance, to set a private field to a value that is dependent on one of the incoming properties.
There’s also the SetParametersAsync method (the synchronous version of this method is called SetParameters), which can be used if you would like to do something before the parameters passed to the component are assigned to the properties.
Code Listing 4-l: Implementing SetParametersAsync
public override Task SetParametersAsync(ParameterView parameters) { return base.SetParametersAsync(parameters); } |
Notice that this method must be public rather than protected. The incoming parameters can be inspected by looking at the parameters argument, which is of ParameterView type.
It’s important to know that the assignment of the incoming parameters to the properties of the component is done by the base implementation of the method.
This means that if the call to the base implementation of the method is omitted, the incoming parameters won’t be assigned to the component properties.
There’s also the OnAfterRenderAsync (the synchronous version of this method is called OnAfterRender). When this method gets triggered, the component has already been re-rendered.
This method also fires the first time the component renders, so in that case the firstRender argument passed to this method has a value of true.
Code Listing 4-m: Implementing OnAfterRenderAsync
protected override Task OnAfterRenderAsync(bool firstRender) { return base.OnAfterRenderAsync(firstRender); } |
You can use this method if you would like to do something with the rendered elements, such as using a specific JavaScript library that uses them.
Then, we also have the ShouldRender method. If false is returned from this method, it can prevent the component from re-rendering.
Code Listing 4-n: Implementing ShouldRender
protected override bool ShouldRender() { return base.ShouldRender(); } |
Blazor ignores this method when a component is initially rendered.
One important thing to keep in mind when working with life cycle methods is that Blazor’s diff mechanism is in charge, so it will always try to update the element that changed within a list of elements.
Blazor’s diff mechanism does its best to map objects and elements that have changed on its own, but the way it does this is not perfect.
This means that sometimes the diff mechanism might lose track of what changes have taken place and it might not re-render an element that changed, but instead re-render the whole list of elements the changed element is part of.
When situations like these occur, they could lead to unexpected side effects and affect the user experience with the application.
This not only applies to changes of a specific element within a list, but also to operations such as insertion or deletion of elements in a list.
In such cases, when something goes wrong, the diff mechanism will have no idea where to place the new element.
So, to prevent these types of situations, we as developers can provide some help to the diff mechanism in the way we write our code.
The way we can do this is by adding a key to each element in the list. The key should be unique for each element. So, it can be a unique identifier that doesn’t repeat itself, or it can be an object reference.
The diff mechanism works not only by comparing unique values for keys but also by comparing different object references. Let’s go ahead and implement this feature to see how simple it is.
Using Solution Explorer, open the FetchData.razor file that resides in the Pages folder. There, find the reference to the ForecastRow object.
<ForecastRow Forecast="forecast" />
Let’s change it to this.
<ForecastRow @key="forecast" Forecast="forecast" />
That’s all—if you now run the application with Ctrl+F5 and navigate to the Weather forecast page, you will see that everything works as usual.
To understand the simplicity and power of two-way data binding, let’s add an input check box to the extra details section. This check box will have the same value as Forecast.ShowExtras.
So, when the arrow button is clicked, the extra details section for that row will be shown, and the check box will be selected. When the check box is cleared, the row will be hidden.

Figure 4-d: Extras Section with Check Box Bound to Forecast.ShowExtras
So, how can we achieve this with what we already know? Using Solution Explorer, go to the Components folder and open the ForecastRow.razor file.
Let’s update the existing code with the following. The changes are highlighted in bold.
Code Listing 4-o: Updated ForecastRow.razor
@inherits ForecastRowBase <tr> <td>@Forecast.Date.ToShortDateString()</td> <td>@Forecast.TemperatureC</td> <td>@Forecast.TemperatureF</td> <td>@Forecast.Summary</td> <div @onclick="e => Forecast.ShowExtras = !Forecast.ShowExtras"> <IconButton aria-label="delete" Style="margin: 8px;" Size="@IconButtonSize.Small"> <ArrowDownwardIcon Style="font-size: inherit;" /> </IconButton> </div> </tr> @if (Forecast.ShowExtras) { <tr> <td> <strong>Humidity:</strong> @Extras.Humidity% </td> <td> <strong>Wind:</strong> @Extras.Wind </td> <td> <input type="checkbox" checked="@Forecast.ShowExtras" @onchange="e => base.CheckboxChanged(e)"/> </td> </tr> } |
As we can see from the preceding listing, we’ve added another element to the extras section, an input check box, bound to Forecast.ShowExtras, which is set when the arrow button is clicked.
The check box has also an onchange event that is triggered every time the check box is clicked. This invokes the CheckboxChanged method from the base class of the ForecastRow component, which is ForecastRowBase.
Next, let’s add the CheckboxChanged method to the ForecastRow.razor.cs file. The updated code for the ForecastRowBase class follows, and the changes are highlighted in bold.
Code Listing 4-p: Updated ForecastRow.razor.cs
using BlazorApp.Data; using Microsoft.AspNetCore.Components; using System.Threading.Tasks; namespace BlazorApp.Components { public class ForecastRowBase: ComponentBase { [Parameter] public WeatherForecast Forecast { get; set; } protected WeatherExtras Extras { get; set; } [Inject] public WeatherExtrasService ExtrasService { get; set; } protected async override Task OnInitializedAsync() { Extras = await ExtrasService.GetExtrasAsync(Forecast); } public void CheckboxChanged(ChangeEventArgs e) { var n = (bool)e.Value; Forecast.ShowExtras = n; } protected override Task OnParametersSetAsync() { return base.OnParametersSetAsync(); } public override Task SetParametersAsync(ParameterView parameters) { return base.SetParametersAsync(parameters); } protected override Task OnAfterRenderAsync(bool firstRender) { return base.OnAfterRenderAsync(firstRender); } protected override bool ShouldRender() { return base.ShouldRender(); } } } |
Let’s analyze what the CheckboxChanged method does. First, the current value of the check box is assigned to the n variable.
var n = (bool)e.Value
Then, the value of the n variable is assigned to Forecast.ShowExtras. This is what hides the extras section for that row.
So, to see this in action, run the application with Ctrl+F5 and navigate to the Weather forecast page. Then, click on any of the arrow buttons in the data table.

Figure 4-e: Extras Section Shown
After the data row is displayed, click on the check box. As soon as you do that, the extras section for that row will be hidden.
Awesome—this solution works. However, we have not implemented two-way binding yet, because we had to add the CheckboxChanged method to be able to change the value of the Forecast.ShowExtras property.
Implementing two-way data binding happens when the value of the check box automatically changes the value of the Forecast.ShowExtras property, without the need to use a method.
So, how can we achieve this with Blazor? We need to use the bind directive. Let’s update the ForecastRow.razor code to do that. Let’s replace the following code:
<input type="checkbox" checked="@Forecast.ShowExtras"
@onchange="e => base.CheckboxChanged(e)"/>
With this:
<input type="checkbox" @bind="Forecast.ShowExtras" />
So, what happened to the onchange event? Well, that’s the beauty of two-way data binding—we don’t need to invoke the CheckboxChanged method anymore.
We could even remove the CheckboxChanged method from the ForecastRowBase class, but let’s leave it.
If you run the application by pressing Ctrl+F5, navigate to the Weather forecast page, click on any of the arrows, and then click on the check box, you’ll see that it works as expected. That’s the magic of two-way data binding.
On some occasions, parent components need to be notified of events that happen in a child component so that the parent component can execute a specific action. This is where event callbacks come in handy.
For example, let’s add a feature to our application that notifies the parent component, FetchData, and executes an action, such as displaying a notification or dialog, whenever the user clicks on the check box that is part of the ForecastRow component.
Of course, for this specific example, we could build the notification mechanism within the ForecastRow component. However, the idea is to showcase how a child component can notify a parent component that an event has taken place, so the parent component can execute an action.
Let’s go ahead and implement this. Using Solution Explorer, go to the Components folder and open the ForecastRow.razor.cs file.
Replace the existing code with the following. The changes are highlighted in bold.
Code Listing 4-q: Updated ForecastRow.razor.cs
using BlazorApp.Data; using Microsoft.AspNetCore.Components; using System.Threading.Tasks; namespace BlazorApp.Components { public class ForecastRowBase: ComponentBase { [Parameter] public WeatherForecast Forecast { get; set; } protected WeatherExtras Extras { get; set; } [Inject] public WeatherExtrasService ExtrasService { get; set; } [Parameter] public EventCallback<bool> OnShowHide { get; set; } protected async override Task OnInitializedAsync() { Extras = await ExtrasService.GetExtrasAsync(Forecast); } public void CheckboxChanged(ChangeEventArgs e) { var n = (bool)e.Value; Forecast.ShowExtras = n; } public void CheckboxClicked() { OnShowHide.InvokeAsync(Forecast.ShowExtras); } protected override Task OnParametersSetAsync() { return base.OnParametersSetAsync(); } public override Task SetParametersAsync(ParameterView parameters) { return base.SetParametersAsync(parameters); } protected override Task OnAfterRenderAsync(bool firstRender) { return base.OnAfterRenderAsync(firstRender); } protected override bool ShouldRender() { return base.ShouldRender(); } } } |
If we carefully look at the preceding code, we can see that there are two new additions to the code.
The first one to notice is the EventCallback property called OnShowHide, which is annotated with the Parameter attribute.
[Parameter]
public EventCallback<bool> OnShowHide { get; set; }
As you know, the Parameter attribute indicates that the property can be passed from the parent to the child component.
The child component is where the property resides, which in this case is the ForecastRow component. The property is declared within the ForecastRowBase class, which ForecastRow inherits from.
The EventCallback is a delegate that indicates that the parent component (in our case the FetchData component) must implement an event to trigger the execution of a method when the child component changes the value of a property.
So, in other words, when the value of the OnShowHide property changes in the ForecastRow component, the parent component FetchData gets notified of this change and executes a method to perform some action.
Now, you might be asking yourself, what makes the value of the OnShowHide property change? That’s the responsibility of the CheckboxClicked method.
public void CheckboxClicked()
{
OnShowHide.InvokeAsync(Forecast.ShowExtras);
}
The CheckboxClicked method invokes the OnShowHide.InvokeAsync method, passing the value of Forecast.ShowExtras as a parameter. This is possible because OnShowHide is a delegate of type EventCallback.
The following diagram illustrates how this sequence of events happens between the ForecastRow and FetchData components.

Figure 4-f: EventCallback Sequence (ForecastRowBase–FetchData)
But another question arises. What triggers the execution of the CheckboxClicked method? For that to happen, we need to bind the CheckboxClicked method to the input check box component.
To do that, using Solution Explorer, navigate to the Components folder and double-click on the ForecastRow.razor file to open it.
Replace the existing code with the following. The change is highlighted in bold.
Code Listing 4-r: Updated ForecastRow.razor
@inherits ForecastRowBase <tr> <td>@Forecast.Date.ToShortDateString()</td> <td>@Forecast.TemperatureC</td> <td>@Forecast.TemperatureF</td> <td>@Forecast.Summary</td> <div @onclick="e => Forecast.ShowExtras = !Forecast.ShowExtras"> <IconButton aria-label="delete" Style="margin: 8px;" Size="@IconButtonSize.Small"> <ArrowDownwardIcon Style="font-size: inherit;" /> </IconButton> </div> </tr> @if (Forecast.ShowExtras) { <tr> <td> <strong>Humidity:</strong> @Extras.Humidity% </td> <td> <strong>Wind:</strong> @Extras.Wind </td> <td> <input type="checkbox" @bind="Forecast.ShowExtras" @onclick="base.CheckboxClicked" /> </td> </tr> } |
Notice that we now have the CheckboxClicked method bound to the onclick event of the check box component.
This means that when the check box component is clicked, the CheckboxClicked method will initiate an event that is going to be received by the parent component, FetchData, which then executes a method. Let’s have a look at it.
So, using Solution Explorer, open the FetchData.razor file that resides in the Pages folder and replace the existing code with the code from the following listing. The changes are highlighted in bold.
Code Listing 4-s: Updated FetchData.razor
@page "/fetchdata" @using BlazorApp.Data @inject WeatherForecastService ForecastService @using Microsoft.Extensions.Logging; @inject ILogger<Counter> logger; <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <ForecastRow @key="forecast" Forecast="forecast" OnShowHide="ShowHide" /> } </tbody> </table> } @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } protected void ShowHide(bool toggle) { logger.LogWarning("Event callback triggered..."); } } |
Let’s have a look at what we have done. First, we imported the Logging namespace and injected the logger service into this component by invoking the following instructions.
@using Microsoft.Extensions.Logging;
@inject ILogger<Counter> logger;
The only reason for doing this is that when the event is triggered, we want to log a message to the output console.
Next, we added the OnShowHide parameter to the ForecastRow component and assigned it to the ShowHide method.
<ForecastRow @key="forecast" Forecast="forecast" OnShowHide="ShowHide" />
After that, we implemented the ShowHide method in the @code section of the FetchData page component.
Notice that the ShowHide method receives as a parameter the Boolean value (toggle) that was passed to the OnShowHide.InvokeAsync method, which is the value of Forecast.ShowExtras.
All this method does is write a message to the output console when OnShowHide is triggered.
Because we are just using this method to write to the console, we don’t need to use the value of the toggle parameter. In any case, it is good to have it for future use.
For Blazor Server applications, the logger object writes messages to the output console of Visual Studio, whereas for Blazor WebAssembly applications, the message would appear on the browser’s developer tools console.
So, to see this in action, let’s run the application. This time, let’s execute it in full debug mode by pressing F5 and not Ctrl+F5, so we can see the message on the output console in Visual Studio when the event is triggered.
Once the application is running, navigate to the Weather forecast page and click on one of the arrow buttons. Once the extras section is displayed, click on the check box. You should then see the message written to the output console.

Figure 4-g: The Event Callback Message
Awesome, we have now seen how event callbacks can be used in Blazor to execute an action on a parent component that has been initiated from a change within a child component.
So far in all the examples we have written, we’ve passed to our custom components, such as ForecastRow, a couple of parameters max.
However, in a real-life application, we could encounter a situation where a child component might require many parameters to be passed from the parent component. The more parameters to pass, the trickier it becomes to manage them.
Let’s take this hypothetical situation, for instance, where we need to call the ForecastRow component from the parent FetchData component, as follows.
<ForecastRow @key="forecast" Forecast="forecast" Region="reg" City="city"
State="state" Day="day" Month="month" Year="year" OnShowHide="ShowHide" />
This means that the ForecastRowBase class would look as follows.
[Parameter]
public string Region { get; set; }
[Parameter]
public string City { get; set; }
[Parameter]
public string State { get; set; }
[Parameter]
public string Day { get; set; }
[Parameter]
public string Month { get; set; }
[Parameter]
public string Year { get; set; }
[Parameter]
public WeatherForecast Forecast { get; set; }
What happens if the application requirements change and we need to add more parameters to the ForecastRow component?
You can start to see where this might be going, and the code may start to become messy and hard to manage fairly quickly.
Luckily, Blazor has a feature that can allow us to combine multiple parameters into one, making it much easier to manage how we can invoke a component like this one.
The way to do this is by using a Dictionary that combines all parameters into one, which would be as follows.
[Parameter]
public Dictionary<string, object> InputAttributes { get; set; } =
new Dictionary<string, object>()
{
{ "Region", "" },
{ "City", "" },
{ "State", "" },
{ "Day", "" },
{ "Month", "" },
{ "Year", "" }
};
The key of the Dictionary is of type string and the value of type object. The names of the parameters become the keys within the Dictionary, and if we wanted to, we could give those keys default values, which in this case are empty.
So, once we have this Dictionary, we can invoke the child component by removing all the other parameters and replacing them with @attributes="InputAttributes". Let’s have a look.
<ForecastRow @key="forecast" Forecast="forecast"
InputAttributes="inputAttributes" OnShowHide="ShowHide" />
Although we won’t be doing anything with these extra parameters, let’s update our code so we can have them ready just in case they would be required in the future.
So, let’s open the ForecastRow.razor.cs file that resides in the Components folder and replace the existing code with the following. The changes are highlighted in bold.
Code Listing 4-t: Updated ForecastRow.razor.cs
using BlazorApp.Data; using Microsoft.AspNetCore.Components; using System.Collections.Generic; using System.Threading.Tasks; namespace BlazorApp.Components { public class ForecastRowBase: ComponentBase { [Parameter] public Dictionary<string, object> InputAttributes { get; set; } = new Dictionary<string, object>() { { "Region", "" }, { "City", "" }, { "State", "" }, { "Day", "" }, { "Month", "" }, { "Year", "" } }; [Parameter] public WeatherForecast Forecast { get; set; } protected WeatherExtras Extras { get; set; } [Inject] public WeatherExtrasService ExtrasService { get; set; } [Parameter] public EventCallback<bool> OnShowHide { get; set; } protected async override Task OnInitializedAsync() { Extras = await ExtrasService.GetExtrasAsync(Forecast); } public void CheckboxChanged(ChangeEventArgs e) { var n = (bool)e.Value; Forecast.ShowExtras = n; } public void CheckboxClicked() { OnShowHide.InvokeAsync(Forecast.ShowExtras); } protected override Task OnParametersSetAsync() { return base.OnParametersSetAsync(); } public override Task SetParametersAsync(ParameterView parameters) { return base.SetParametersAsync(parameters); } protected override Task OnAfterRenderAsync(bool firstRender) { return base.OnAfterRenderAsync(firstRender); } protected override bool ShouldRender() { return base.ShouldRender(); } } } |
Notice that the only changes we have made are adding the reference to the Generic namespace, declaring the InputAttributes as a Dictionary, and decorating it with the Parameter attribute.
Then, on the parent component FetchData, the code also needs to be replaced and updated as follows. The changes are highlighted in bold.
Code Listing 4-u: Updated FetchData.razor.cs
@page "/fetchdata" @using BlazorApp.Data @inject WeatherForecastService ForecastService @using Microsoft.Extensions.Logging; @inject ILogger<Counter> logger; <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <ForecastRow @key="forecast" Forecast="forecast" InputAttributes="inputAttributes" OnShowHide="ShowHide" /> } </tbody> </table> } @code { private WeatherForecast[] forecasts; private Dictionary<string, object> inputAttributes; protected override async Task OnInitializedAsync() { inputAttributes = new Dictionary<string, object>() { { "Region", "Spain" }, { "City", "Valencia" }, { "State", "Comunidad Valenciana" }, { "Day", "01" }, { "Month", "11" }, { "Year", "2020" } }; forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } protected void ShowHide(bool toggle) { logger.LogWarning("Event callback triggered..."); } } |
So, by looking at this code, we can see that the first change has to do with assigning the value of the inputAttributes (which is declared in the @code section of the component) to the child component, ForecastRow.
<ForecastRow @key="forecast" Forecast="forecast"
InputAttributes="inputAttributes" OnShowHide="ShowHide" />
Next, inputAttributes is declared as a Dictionary in the @code section.
private Dictionary<string, object> inputAttributes;
Following that, within the OnInitializedAsync method, inputAttributes is initialized with default values.
If we now run the application using Ctrl+F5, everything should continue to work as expected. There won’t be any visible changes to the UI of the application, though.

Figure 4-h: The App Running as Expected (Weather forecast)
So, by using this technique, Blazor offers us a way to easily handle and effectively manage child components that have many parameters.
There’s one last technique I’d like to talk about that can also come in handy when working with real-life projects. That is to get a reference to a child component that can be used to access members of it within a parent component.
Let’s say that we want to create a new component called CoolDialog to display a fancy Material Design dialog box, which is going to be our child component.
Then we want to be able to invoke one of the methods within the CoolDialog child component that will display the dialog, but we want to invoke that method from the parent component—in this case from the FetchData page component. So, how can we do this?
Using Solution Explorer, click on the Components folder, right-click, click Add, then click Razor Component. Let’s call the file CoolDialog.razor and then click Add.
Once the file has been created, let’s add the following code to it.
Code Listing 4-v: CoolDialog.razor
@inherits CoolDialogBase <Skclusive.Material.Dialog.Dialog Open="@base.Open" OnClose="@base.OnClose" aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description"> <DialogTitle id="alert-dialog-title"> Do you like this dialog? </DialogTitle> <DialogContent> <DialogContentText id="alert-dialog-description"> This is a dialog from the Skclusive-UI library :) </DialogContentText> </DialogContent> <DialogActions> <Button OnClick="@base.OnClose" Color="@Color.Primary"> Disagree </Button> <Button OnClick="@base.OnClose" Color="@Color.Primary" autofocus> Agree </Button> </DialogActions> </Skclusive.Material.Dialog.Dialog> |
The preceding code simply uses components from the Skclusive-UI library to create the HTML markup for a dialog box.
From this code, two things stand out right away. One is that the code references a property called base.Open, and the other is that there’s a reference to a base.OnClose method.
Both refer to the base class of this component, which we haven’t created yet and is going to be called CoolDialogBase, which the component inherits from (@inherits CoolDialogBase). So, let’s create the base class and code-behind file for this component.
Using Solution Explorer, click on the Components folder, right-click, click Add, then click New Item. From the list of items, choose Class.
Let’s call the file CoolDialog.razor.cs and then click Add. Once the file has been created, open it and add the following code.
Code Listing 4-w: CoolDialog.razor.cs
using Microsoft.AspNetCore.Components; namespace BlazorApp.Components { public class CoolDialogBase: ComponentBase { protected bool Open { set; get; } public void OnClose() { Open = false; StateHasChanged(); } public void OnOpen() { Open = true; StateHasChanged(); } } } |
Following the same naming convention we’ve used all along with this book, we’ll add the suffix Base to the name of the class. So, in this case, the class is called CoolDialogBase.
The code is very simple, and there are two methods for opening (OnOpen) and closing (OnClose) the dialog box, as well as a Boolean property (Open) that indicates whether the dialog is open or closed.
When the opening (OnOpen) and closing (OnClose) methods are executed, the component’s state is changed, which is done by invoking the StateHasChanged method.
Great—we now have a dialog box we can use within our application. So, to use it, go to Solution Explorer and under the Pages folder, open the FetchData.razor file.
Once the file is open, replace the existing code with the following. The changes have been highlighted in bold.
Code Listing 4-x: Updated FetchData.razor
@page "/fetchdata" @using BlazorApp.Data @inject WeatherForecastService ForecastService @using Microsoft.Extensions.Logging; @inject ILogger<Counter> logger; <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <ForecastRow @key="forecast" Forecast="forecast" InputAttributes="inputAttributes" OnShowHide="ShowHide" /> } </tbody> </table> <CoolDialog @ref="coolDlg" /> } @code { private WeatherForecast[] forecasts; private Dictionary<string, object> inputAttributes; private CoolDialogBase coolDlg;
protected override async Task OnInitializedAsync() { inputAttributes = new Dictionary<string, object>() { { "Region", "Spain" }, { "City", "Valencia" }, { "State", "Comunidad Valenciana" }, { "Day", "01" }, { "Month", "11" }, { "Year", "2020" } }; forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } protected void ShowHide(bool toggle) { logger.LogWarning("Event callback triggered..."); coolDlg.OnOpen(); } } |
The first change to the code that you’ll notice is that the CoolDialog component has been added after the <table/> tag.
<CoolDialog @ref="coolDlg" />
The interesting thing about the way that this component has been added to the HTML markup is that a reference to an instance of the CoolDialogBase class is included in the markup.
This means that we can invoke accessible properties or methods of the CoolDialogBase class by using the coolDlg instance, which is declared within the @code section, as follows.
private CoolDialogBase coolDlg;
Although the component has been added to the HTML markup, it won’t be visible by default when the application runs.
Finally, within ShowHide, the OnOpen method of the CoolDialogBase instance is invoked. So, that’s it—we are ready to test the dialog box functionality using a child component reference from a parent component.
Let’s run the application with Ctrl+F5, navigate to the Weather forecast page, click on one of the arrows, and then when the extras section is open, click on the check box.
Once the check box has been clicked, the dialog box will be displayed, which we can see in the following figure.

Figure 4-i: The CoolDialog Component (Invoked as a Reference from FetchData)
To close the dialog, you can click on any of the buttons. Awesome.
We’ve reached the end of this chapter. We explored many of the features and techniques in Blazor that allow us to compose an application by using various component techniques.
By having to compose components, we’ve seen the various ways data can pass from one component to another and how events can be cascaded from a child to a parent component, as well as how a parent component can invoke methods and properties contained within the child.
These techniques are essential for creating any Blazor application that should be as modular and reusable as possible using components.
In the next and final chapter, we will briefly explore the template syntax so you can create component templates.