Understanding page routing in Blazor
Blazor WebAssembly is a SPA. This means that routing is not done on the server but the client. While clicking on the link updates the address bar of the browser, the page itself is not refreshed. Blazor finds a component with a matching route in the @page
directive and renders this component (and all child components) as a page.
The Router
component takes care of resolving the correct component to render. This component is typically used in the application root component – that is, App.razor
. The component is created from two RenderFragments, as follows:
<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
In the preceding code, you can see the Router
component with the Found
and NotFound
sections. The Router
component looks to the specified AppAssembly
and discovers all routable components (components with the @page
directive). When navigating from one page to another, the Router
component renders the Found
section, if there is any routable component with a matched route. Otherwise, the NotFound
section is rendered.
Rendering of the component is handled in the RouteView
component. The RouteData
parameter of the component contains information about the matched component. RouteView
uses a layout defined on the component’s @layout
directive, or DefaultLayout
if the component layout is not specified.
The FocusOnNavigate
component is not required. This component sets focus on the element specified by the CSS selector in the Selector
parameter. It is useful when building websites compatible with screen readers, or when you want to focus on some user input fields after changing the page.
Navigating between pages
To navigate between pages, the standard HTML anchor is used:
<a href="/contacts">Contacts</a>
As shown in the preceding code, there is no difference between page navigation in standard HTML and Blazor. That is one of Blazor’s advantages over other JavaScript SPA frameworks, which use the custom component to provide SPA navigation. Blazor intercepts any navigation on the same site and tries to find the corresponding components to render.
If you need to manipulate the address from the C# code, you must inject NavigationManager
into your code, as follows:
@page "/offers" @inject NavigationManager NavManager … <button @onclick="GoToContacts">Contacts</button> @code { void GoToContacts() { NavManager.NavigateTo("contacts"); } }
The preceding code will change the page to /contacts
after the button is clicked.
Page directive
The @page
directive can be specified multiple times in the component. It can also contain dynamic parts of the URL:
PageDirective.razor
@page "/author" @page "/author/{Name}" <h1>Author @authorName </h1> @code { private string authorName; [Parameter] public string Name { get; set; } protected override void OnInitialized() { authorName = Name ?? "Not set"; } }
In the preceding code, the first @page
directive specifies the URL without any parameters. The second @page
directive specifies a parameter with the name. The following URLs are valid for the PageDirective
component:
/author
/author/John
/author/123
In the third example, /author/123
, you can see that we can pass numbers to the text parameter. It is fine here because any value can be treated like a string. But what if we expect a different type?
Route constraints
Route constraints are used when you need to enforce a specific data type of the route parameter. The route constraints are defined the same way as in C# API endpoints – that is, by adding a semicolon after the parameter name and then specifying the data type:
@page "/author/{Id:int}"
The preceding code will define the URL for the author with Id
as an integer.
Not all constraints are supported at the time of writing. The following table shows the supported types:
Constraint type |
Example of constraint |
Example of valid values |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Table 2.2 – Blazor router supported constraints
Component route parameters can also be marked as optional:
@page "/user/{id:int}/{valid:bool?}"
The preceding code specifies the URL to /user
with the required Id
parameter and optional Valid
bool parameter. The optional ?
symbol can also be used without type constraints.
Catch-all parameters
Sometimes, you may need to create components for multiple routes. In that case, a catch-all parameter can be used:
Posts/AllPosts.razor
@page "/posts/{*pageRoute}" @code { [Parameter] public string? PageRoute { get; set; } }
The preceding code shows a catch-all parameter named PageRoute
. The catch-all route parameter must be of the string
type and placed at the end of the route; it is not case-sensitive.
The PageRoute
parameter will contain all the matched values from the URL:
- For the
/posts/animals/africa/forest
URL, thePageRoute
value isanimals/africa/forest
- For the
/posts/animals
URL, thePageRoute
value isanimals
- For the
/posts/animals/europa%2Fwater
URL, thePageRoute
value isanimals/europa/water
With that, we know how to create components and how to navigate between them. In the next section, we will look at the project we will be creating in this book. And yes! We will use everything we’ve learned in this chapter up until this point.