Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
vermeir_nico_introducing_net_6_getting_started_with_blazor_m.pdf
Скачиваний:
19
Добавлен:
26.06.2023
Размер:
11.64 Mб
Скачать

Chapter 5 Blazor

Another rootcomponent being added is the HeadOutlet. Its selector specifies that the component is added to the head section of the HTML file rather than replacing it. The component itself allows us to easily set the title of the page to be reflected in the browser’s tab or title bar.

After adding the root components, we register an HTTP client as a scoped service into .NET’s dependency injection framework. We are registering our services here so that we can inject them in our Blazor pages later on as we will see in a minute.

Blazor Component System

As we have seen in Listing 5-4, the App.razor component declares a Router. That Router loads MainLayout.razor. MainLayout.razor is a component that can be best viewed as the application’s template. Listing 5-5 shows the default MainLayout.razor.

Listing 5-5.  MainLayout.razor

@inherits LayoutComponentBase

<div class="page">

<div class="sidebar"> <NavMenu />

</div>

<main>

<div class="top-row px-4">

<a href="https://docs.microsoft.com/aspnet/" target="_blank"> About</a>

</div>

<article class="content px-4"> @Body

</article>

</main>

</div>

134

Chapter 5 Blazor

The layout specifies where the navigation menu lives, what the grid layout of the page is, and so on. The NavMenu element specified in MainLayout is actually another Blazor component living in our project. You can find it in the Shared folder. Blazor components can be embedded in each other in a very similar way as HTML elements are added on a page. We can even add properties to a component, mark them as Parameter with an attribute, and pass data between components this way. An example of this is included in the default template in the Shared folder. Listing 5-6 shows the SurveyPrompt property as parameter declaration.

Listing 5-6.  Adding parameters to a Blazor component

[Parameter]

public string? Title { get; set; }

That parameter can be set as if it was a property on an HTML element as demonstrated in Listing 5-7.

Listing 5-7.  Setting a parameter

<SurveyPrompt Title="How is Blazor working for you?" />

MainLayout also contains an @Body placeholder. This placeholder is where all our Blazor pages will be inserted into the layout of the application. Choosing what page to have as initial page is as simple as setting a page’s route to “/”. This is often Index.razor, as is the case in the default template. Listing 5-8 shows a snippet of the default Index. razor page.

Listing 5-8.  Index.Razor’s page declaration

@page "/"

<PageTitle>Index</PageTitle>

Since the url to this page is the root url of the application, this page will be initially loaded and placed on the @Body part of MainLayout. The PageTitle we specify here is what will go in the HeadOutlet root component we declared in Program.cs and will become the page title.

If we now go back to the counter page we have previously seen, you can clearly see where the navigation menu comes from and what part of the MainLayout is replaced with the code from the counter component.

135

Chapter 5 Blazor

Creating Blazor Pages

We have mentioned both Blazor pages and Blazor components before. Blazor pages are components with an @page directive, giving them a url for the routing system to navigate to.

Let’s jump to FetchData.razor, starting with the attributes at the top of the file, listed in Listing 5-9.

Listing 5-9.  Attributes in FetchData.razor

@page "/fetchdata" @inject HttpClient Http

We’ve run into the page attribute before. As a reminder, it configures the route for this page; when navigating to https://<hostname:port>/fetchdata, we will be redirected to this page. The inject attribute is Blazor’s version of dependency injection. It will search for the HttpClient type in the ServiceCollection that we’ve seen in Listing 5-3 and set an instance of that type to the Http member.

In Listing 5-6, we see the Razor code of the FetchData class. As mentioned before, Razor can be explained by HTML mixed C# snippets. Take the if statement for example. It is prefixed with an @ sign, signaling that a code statement will follow. The statement is followed by brackets, embedding the code between the brackets in the statement. This specific statement will prevent a potential NullReferenceException on forecasts. The code loops over the forecasts collection, but if that collection is not loaded yet, it will crash. The interesting part here is that that if statement will actually get reevaluated as soon as the forecasts collection is updated. This is because Blazor has an internal method called StateHasChanged. When this method is triggered, Blazor will re-render its current state, take a diff between the old and the new render tree, and apply that

diff to the old one that is still on screen. This results in only a partial refresh of the page instead of a full page reload. StateHasChanged can be called manually by us, but Blazor calls it internally whenever a property on a component changes or when an event on a component is triggered. This results in an easy way to show a “loading” message while data is being fetched from an API.

The foreach statement loops over the forecasts collection and lists its content in an HTML table. This is a great example of how flexible Razor can mix HTML elements with .NET data. We can even call member methods, like the ToShortDateString on the

DateTime struct.

136

Chapter 5 Blazor

Listing 5-10. Razor code

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</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>

}

137

Chapter 5 Blazor

The final part of the FetchData file is the code block. Every Razor file can contain a @code block. This contains most of the logic of a Razor component, and its lifecycle methods.

Listing 5-11.  Code block in a Razor file

@code {

private WeatherForecast[]? forecasts;

protected override async Task OnInitializedAsync()

{

forecasts = await Http.GetFromJsonAsync<WeatherForecast[]> ("sample-data/weather.json");

}

public class WeatherForecast

{

public DateTime Date { get; set; }

public int TemperatureC { get; set; }

public string? Summary { get; set; }

public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

}

}

The most interesting part of Listing 5-11 is OnInitializedAsync. This is one of seven lifecycle methods of a Blazor component. The complete list of lifecycle methods is:

•\ OnInitialized

•\ OnInitializedAsync

•\ OnParametersSet

•\ OnParametersSetAsync

•\ OnAfterRender

•\ OnAfterRenderAsync

•\ ShouldRender

138

Chapter 5 Blazor

As you can see, every lifecycle event, except for ShouldRender, has both a synchronous and an asynchronous version. If you are hooking into one of the lifecycle events and you need to call an async method, you need to use the async version of the lifecycle method. We can even use both the async and the sync version of these lifecycle methods at the same time; they will both execute but there is no guarantee that they will always run or finish in the same order. OnInitialized is called after the component is done loading and all UI components are available; this is the perfect lifecycle event to load initial data from a data service. OnParametersSet is called after parameters change value, usually due to another component that updates the parameter on the current component. OnAfterRender is called after everything is done rendering, but other initializations might still be happening inside other components that we are referencing. And finally, ShouldRender is called right before rendering happens. Microsoft has a nice diagram showing the component lifecycle. I have added that diagram for your reference as Figure 5-6.

139