Use the Query String to pass an array of selected values between Blazor Pages
April 28, 2022 · 5 minute read · Tags: blazor
A common requirement with Blazor (or any web app) is to pass state from one component to another (or indeed from one page to another).
On example of this is when you want your users to make a selection, then use that to populate or filter data on another page.
For example, let’s say you want to offer your users a selection of movies to choose from.
Once they’ve made their selection you want to offer recommendations based on the movies they chose.
Pass state between ‘Pages’
Sometimes you’ll just want to take their selections and hand them off to another component on the same page.
For this you can use plain old component parameters (or, depending on how your components are arranged, cascading parameters).
But what if you want to navigate to a different ‘page’ to show the recommendations.
Well it turns out there are numerous ways to pass state around in your Blazor app.
Here are some options:
- Pass state via the URL using route parameters
- Pass state via the URL using the query string
- Use a state ‘service’ to keep track of the selected items (essentially an injected singleton which each page/component can then interact with)
- Use a full blown state management library (e.g. Fluxor)
- Use browser storage to keep track of the selected items
- Record the user’s selections in a database
These are all valid options, and which one you choose is largely dependant on some additional factors, such as:
- Is the state transient (no requirement for it to be tracked when the user closes their browser)
- Is it important to record the user’s selection, for use at a later date (in which case the DB option may be the best)
- Do you want the user to be able share their selection with others?
That last factor leads us into the concept of deep linking, which is what we’ll focus on for the rest of this article.
Deep linking
Whilst there are lots of ways to pass state, if you want the URL to reflect that state you’re limited to using Route Parameters or the Query String.
The main reason to use the URL is to support deep linking, whereby the user can share a link based on their current selections and those selections will be restored when someone visits that same link.
To make that work you need to keep track of the user’s selections, then pass them along in the URL when they attempt to navigate to the next page.
Taking our movie example, here’s a component which shows a list of possible movies to choose from:
@page "/state"
@inject MovieDatabase MovieDatabase
@inject NavigationManager NavigationManager
<h2>Select your favourites from the following films</h2>
<ul>
@foreach (var film in possibleFilms)
{
<li>
<input type="checkbox" @onchange="e => FilmSelected(film, e)"/>
@film.Name
</li>
}
</ul>
<button onclick="@CompareFilms">Compare</button>
We’re iterating over the possibleFilms
array, and displaying a checkbox for each one.
When the user has made their selection they’ll hit the Compare button, and that’s when we want to take their choices and put them into the URL.
As far as I can tell there’s no official standard for passing arrays via Query String, but there does seem to be a generally accepted convention:
http://localhost:5000/MovieComparer?film=1&film=2&film=3
The assumption is that we can interpret this query string when we receive it (on a different page) and use it to populate an array with an entry for each ‘film’ (1, 2, 3, etc).
So let’s give that a go, in this case in response to the user clicking that Compare button.
Specifically we need to:
- Transform the list of selected films into a valid Query String
- Navigate to the next page, passing that Query String along for the ride
private async Task CompareFilms()
{
if (selectedFilms?.Any() ?? false)
{
var queryString = string.Join("&", selectedFilms.Select(x=>"film=" + x.Id));
NavigationManager.NavigateTo("/MovieComparer?" + queryString);
}
}
Now you may be wondering if there isn’t a helper method we could have used to get a URL with the correct Query String.
The answer is yes, kind of, but actually no in this case…
There is a helper method called GetUriWithQueryParameters
on Blazor’s NavigationManager
class which you can use to generate a URL with a valid Query String.
However,GetUriWithQueryParameters
expects a dictionary of key/value pairs:
var readOnlyDictionary = new Dictionary<string, object>
{
["film"] = 1,
["film"] = 2,
};
var uri = NavigationManager.GetUriWithQueryParameters(readOnlyDictionary);
A dictionary can only store one value for any given key (by design), so in this case we’d end up with the value 2
in the dictionary and a URI which looks something like this:
http://localhost:5000/MovieComparer?film=2
We want to pass multiple values for the same key (‘film’) hence the need to roll our own Query String with this code:
var queryString = string.Join("&", selectedFilms.Select(x=>"film=" + x));
Receiving Arrays via the Query String
Now for the easiest and quickest part of our little adventure, capturing that array of films from the URL when we navigate to the next page.
.NET 6 introduced the very handy SupplyParameterFromQuery
attribute for your Blazor components.
On our recieving page (‘MovieComparer’) we can use this to populate an array from the Query String:
[Parameter]
[SupplyParameterFromQuery(Name = "film")]
public string[]? Films { get; set; }
SupplyParameterFromQuery
will take each value for ‘film’ from the URL and use it to populate the Films
array.
Summary
You can pass data, including arrays via the Query String when using NavigationManager
to navigate to a different page in your Blazor app.
Blazor ‘routable components’ (that’s pages to you and me) can grab values from the Query String using SupplyParameterFromQuery
, and use those values to populate a parameter.
To send arrays in the Query String you can use this format:
http://localhost:5000/MovieComparer?film=1&film=2&film=3
You can point SupplyParameterFromQuery
at your chosen key (‘film’ in this example) whereby it will read each value for that key and use it to populate an array.
[Parameter]
[SupplyParameterFromQuery(Name = "film")]
public string[]? Films { get; set; }