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 are
orderIdand
itemId. 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 following
blog/{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 case
slug.
- 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-routing
Now 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; } } }
- In the
Pagesdirectory create a file
Products.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); } }
Note how the
@page directive 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> }
By using
NavLink it will create a link the user can click on. Note how
href is pointing to
product.Url. You don't have that property yet so let's fix that. Open up
Data.cs and 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; } } }
-2- Creating a product detail page.
Next, you will create a product detail page.
- Create the file
Product.razorunder
Pagesdirectory. 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; } }
Above you set up a route
@page "/products/{Id:int}". This means you are handling routes like so
/products/1 but 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; }
This 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
/products route using the built-in component
NavLink:
<NavLink href="products" target="_blank">Products</NavLink>
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 run
Yor app should now run at. Let's navigate to. 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 the
Productsclass:
public static Product GetProduct(int id) { return products.SingleOrDefault(p => p.Id == id); }
- Open 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" @ 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"); } }
Now above the declaration of the lifecycle method
OnParametersSet(). This is a point at which
Id has got its value and you can safely write code that asks for a specific
Product with this code:
this.product = Data.Products.GetProduct(this.Id);
Note also how programmatic navigation was added. In the template there is this section:
<button class="btn btn-primary" @ Navigate to products </button>
In the code section there is a method
Navigate(), like so:
private void Navigate() { NavigationManager.NavigateTo("products"); }
What did it get
NavigationManager from? It's added at the of the component like so:
@inject NavigationManager NavigationManager
NavigationManager is 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.WebUtilities
This will install the
WebUtilities package 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);); } this.products = Data.Products.GetProducts(pageParam, pageSizeParam); } }
The 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);
Parse out the parameters. For this you use the
QueryHelperclass with
uri.Queryas input parameter. Then you call
TryGetValue()with the query parameter as an argument. You do this twice, once for
pageand once for
pageSize.); }
- Call
GetProducts()with the query parameters to get a filtered response.
this.products = Data.Products.GetProducts(pageParam, pageSizeParam);
- Open up
Data.csas you will need to update the
GetProducts()method a little. Ensure the
GetProducts()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; }
- Let's test out the solution. Type the following command in the terminal:
dotnet build && dotnet run
- Navigate to. You should now see the 2 first rows displayed like so:
- Change page by changing the
pageto
2so the URL should now look like so. 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.
Discussion (0)