Learn how you manage routing in Blazor for .NET Core and VS Code
Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris
Here's a link to previous works I've written on Blazor. Blazor is a framework that enables you to build full-stack apps in C#. You don't have to mix a backend language with a frontend language like JavaScript. Not having this context switch is a thing that's truly powerful.
References
- Install .NET Core, latest version Ensure you have the latest version of .NET core installed so you have all the Blazor templates installed with it.
- Get started with blazor This is good starter page for Blazor.
- Blazor routing This page shows all you need to know about routing
- Community blog post on Query parameters
- My first article on Blazor
- My second article on Blazor + JavaScript
Routing
Routing, what do we mean by the term? It simply means how we set up navigation and how we deal with the URL of the app. More specifically this usually means the following:
- Setting up simple routes, this involves defining a route like
/productsand determine what component should respond when this route is entered in a Browser. - Dealing with complex routes and route parameters, a complex route usually looks a bit more advanced like so
/orders/{orderId}/items/{itemId}. This route has something called route parameters. Route parameters are parts of the route that instructs the app to get a specific piece of data. With this example, the route parameters areorderIdanditemId. What the parameters convey in this case is that they want a specific order and a specific order item on that order. An example of the mentioned route pattern can look like so/orders/111/item/1. Other examples of complex routes can be the followingblog/{slug}. This is a common pattern when creating a blog. It is the same idea as the order example and also uses route parameters, in this caseslug. - Query parameters, query parameters aren't part of the route technically but is the end of URL. Given a URL
/products?page=1&pageSize=10the query parameters is everything that happens after the?. Parameters are separated by&and are key-value pairs. - Navigation, there are two types of navigation that is interesting to look at:
- Clicking a link, usually there's a specific component helping you produce a link that the user is able to click on.
- Programmatic navigation, this involves navigating with code. This is usually the case if you need to perform some asynchronous work before navigating.
Routing in Blazor
Let's work through a scenario to learn how to handle the most common route aspects. Our scenario is that of master-detail. A product list page and product detail page. We'll gradually add more capabilities to it as we go along.
-1- Adding a product list page
This is a simple page that needs to show a list of products. For data, we will use a simple static data class.
Create a new Blazor project by typing in the following command in the terminal:
dotnet new blazorwasm -o blazor-routing cd blazor-routing1
2Now you have a blazor project ready to go.
Create a file
Data.csin the root of the project and give it the following content:using System.Collections.Generic; namespace Data { public class Product { public int Id { get; set; } public string Name { get; set; } } public static class Products { private static List<Data.Product> products = new List<Data.Product>(){ new Data.Product(){ Id = 1, Name = "Test" }, new Data.Product(){ Id = 2, Name = "Test2" }, new Data.Product(){ Id = 3, Name = "Test3" }, new Data.Product(){ Id = 4, Name = "Test4" } }; public static List<Product> GetProducts() { return products; } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24In the
Pagesdirectory create a fileProducts.razorand give it the following content:@page "/products" @inject NavigationManager NavigationManager <h1>Products</h1> @foreach (var product in products) { <div> <NavLink href="@product.Url" target="_blank">@product.Name</NavLink> </div> } @code { List<Data.Product> products; protected override void OnInitialized() { this.products = Data.Products.GetProducts(pageParam, pageSizeParam); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Note how the
@pagedirective at the top is used to instruct that Blazor that this component should listen to the route/products. You've also set up the navigation between the product list and product detail page. Let's have a closer look at this part:@foreach (var product in products) { <div> <NavLink href="@product.Url" target="_blank">@product.Name</NavLink> </div> }1
2
3
4
5
6By using
NavLinkit will create a link the user can click on. Note howhrefis pointing toproduct.Url. You don't have that property yet so let's fix that. Open upData.csand add it like so:public class Product { public int Id { get; set; } public string Name { get; set; } public string Url { get { return "products/" + this.Id; } } }1
2
3
4
5
6
7
8
9
10
11
12
-2- Creating a product detail page.
Next, you will create a product detail page.
Create the file
Product.razorunderPagesdirectory. Give the file the following content:@page "/products/{Id:int}" @inject NavigationManager NavigationManager <h1>A specific product</h1> This is for Id @Id <div> Here is the product: @product.Name </div> <div> <NavLink href="products" target="_blank">Products</NavLink> </div> @code { [Parameter] public int Id { get; set; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Above you set up a route
@page "/products/{Id:int}". This means you are handling routes like so/products/1but now/products/abc. Note how you are coercing the route parameter to be an integer{Id:Int}. There's also another section of interest where you get hold of the router parameter:[Parameter] public int Id { get; set; }1
2This is great if we in a later stage want to do a data lookup based on the route parameter value. Additionally, note how you add navigation back to the
/productsroute using the built-in componentNavLink:<NavLink href="products" target="_blank">Products</NavLink>1
The href is pointing to products which is your product list page. The NavLink is translated into an anchor tag during compilation.
Try out the application at this point by running the following command in the terminal:
dotnet build && dotnet run1Yor app should now run at
http://localhost:5000. Let's navigate tohttp://localhost:5000/products. You should see the following:
-3- Detail component data lookup
Next, you will implement looking up specific data on the detail component. To do so you need to ensure you have a specific method in Data.cs that lets you select a specific item by id.
Open up
Data.csand add the following method to theProductsclass:public static Product GetProduct(int id) { return products.SingleOrDefault(p => p.Id == id); }1
2
3
4Open up
Product.razorand update the code to look like the following:@page "/products/{Id:int}" @inject NavigationManager NavigationManager <h1>A specific product</h1> This is for Id @Id <div> Here is the product: @product.Name </div> <div> <NavLink href="products" target="_blank">Products</NavLink> </div> <button class="btn btn-primary" @onclick="Navigate"> Navigate to products </button> @code { [Parameter] public int Id { get; set; } Data.Product product = null; protected override void OnParametersSet() { this.product = Data.Products.GetProduct(this.Id); } private void Navigate() { NavigationManager.NavigateTo("products"); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35Now above the declaration of the lifecycle method
OnParametersSet(). This is a point at whichIdhas got its value and you can safely write code that asks for a specificProductwith this code:this.product = Data.Products.GetProduct(this.Id);1Note also how programmatic navigation was added. In the template there is this section:
<button class="btn btn-primary" @onclick="Navigate"> Navigate to products </button>1
2
3In the code section there is a method
Navigate(), like so:private void Navigate() { NavigationManager.NavigateTo("products"); }1
2
3
4What did it get
NavigationManagerfrom? It's added at the of the component like so:@inject NavigationManager NavigationManager1NavigationManageris built into Blazor and helps us navigate among other things.
-4- Adding query parameters
This is great we have our master-detail working. However, it would be great if we learn to use query parameters. So why is that? Query parameters help filter the response. Let's look at a route like so /products?page=1&page=10. What does that mean? The query parameters page and pageSize signals that we want to limit the response. Imagine that /products possibly might return millions of records. We don't want that. So what we do is to think of our data in terms of pages. Having page=1 and pageSize=10 means we want the first 10 records whereas page=2 would mean we want records 11-20.
To start using query parameters we need to both install an assembly and write some code to parse the query parameters from the URL.
In the terminal run the following command:
dotnet add package Microsoft.AspNetCore.WebUtilities1This will install the
WebUtilitiespackage from NuGet.Open up
Products.razorfile. Give it the following content:@page "/products" @inject NavigationManager NavigationManager @using System.Linq <h1>Products</h1> Page: @pageParam PageSize: @pageSizeParam @foreach (var product in products) { <div> <NavLink href="@product.Url" target="_blank">@product.Name</NavLink> </div> } @code { List<Data.Product> products; int? pageParam = null; int? pageSizeParam = null; protected override void OnInitialized() { var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri); if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("page", out var param)) { this.pageParam = Int32.Parse(param); } if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("pageSize", out var param2)) { this.pageSizeParam = Int32.Parse(param2); } this.products = Data.Products.GetProducts(pageParam, pageSizeParam); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34The big addition here was the method
OnInitialized(). There are a few things going on so let's take it line by line.Get the URI, we will need the Uri so it's easier to parse out the query parameters.
var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);1Parse out the parameters. For this you use the
QueryHelperclass withuri.Queryas input parameter. Then you callTryGetValue()with the query parameter as an argument. You do this twice, once forpageand once forpageSize.if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("page", out var param)) { this.pageParam = Int32.Parse(param); } if (Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("pageSize", out var param2)) { this.pageSizeParam = Int32.Parse(param2); }1
2
3
4
5
6
7
8Call
GetProducts()with the query parameters to get a filtered response.this.products = Data.Products.GetProducts(pageParam, pageSizeParam);1
Open up
Data.csas you will need to update theGetProducts()method a little. Ensure theGetProducts()method now looks like so:public static List<Product> GetProducts(int? page, int? pageSize) { if (page.HasValue && pageSize.HasValue) { var filtered = products.Skip((page.Value-1) * pageSize.Value).Take(pageSize.Value); return filtered.ToList(); } return products; }1
2
3
4
5
6
7
8Let's test out the solution. Type the following command in the terminal:
dotnet build && dotnet run1Navigate to
http://localhost:5000/products?page=1&pageSize=2. You should now see the 2 first rows displayed like so:
Change page by changing the
pageto2so the URL should now look like sohttp://localhost:5000/products?page=2&pageSize=2. Your UI should now look like so:
Summary
We've covered quite a few things here related to routing. Basic routing looking like so /products but also more complicated routing like so /products/{id: Int}. Additionally, we even showed how to use query parameters to further filter the response. I hope it was helpful.