How can I access a page's route parameter from the layout?
I have a page that accepts a route parameter like the following:
@page /my-page/{Slug}
I am needing to access the value of Slug
when rendering markup in the shared layout.
I have tried implementing OnParametersSet
in the layout file like the following, but the value is not set. It's only assigned at the page level.
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
@this.Slug <<<<------ display the parameter
</div>
<div class="content px-4">
@Body
</div>
</div>
@code
{
[Parameter]
public string Slug { get; set; }
protected override void OnParametersSet()
{
// Slug is always null :-/
}
}
A Blazor layout is a Razor component that shares markup with components that reference it. Layouts can use data binding, dependency injection, and other features of components. To create a layout component: Create a Razor component defined by a Razor template or C# code.
The cascading parameter approach also has some underlying issues: Blazor WASM - Navigation away from Layout using RouteData creates duplicate components
Another standard means of changing the page behaviour based on our URL is to pass values in the Query String, as in Now, while it is possible to use Query String parameters to adjust your page’s behaviour, sadly in Blazor .NET 5, it is not nearly so easy as route path parameters above.
In Blazor .NET 5, you have to use the NavigationManager class to interact with Query String parameters. This is heavy-handed and difficult to deal with in a page, as the default way to parse out individual query string parameters is (according to Microsoft documentation) is to use the JSInterop to get Javascript to do the work for you. Yuck!
After some muddling around, I have come up with the following solution. It can probably all be done in the .razor
files, but I implemented some of the mess in the "code-behind" files to hide what seems to be a kludge.
In the Layout instance, if you override OnParametersSet
, and drill into Body.Target
you'll find the RouteData
, containing the route parameter(s). You can then propagate these value(s) to the child components in the layout.
Page with a "Slug" parameter we want to make available to the Layout
@page "/mypage/{Slug}"
Layout .razor file
@inherits Components.MyLayoutBase
<div class="sidebar">
<!-- Pass the Slug property to the NavMenu's Parameter -->
<MyNavMenu Slug="@Slug" />
</div>
<div class="main">
<div class="top-row px-4"></div>
<div class="content px-4">
@Body
</div>
</div>
Layout code behind
public class MyLayoutBase : LayoutComponentBase
{
public string Slug { get; set; }
protected override void OnParametersSet()
{
// pull out the "Slug" parameter from the route data
object slug = null;
if ((this.Body.Target as RouteView)?.RouteData.RouteValues?.TryGetValue("Slug", out slug) == true)
{
Slug = slug?.ToString();
}
}
}
Navigation Menu
<div class="top-row pl-4 navbar navbar-dark">
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/foo")">
<span class="oi oi-home" aria-hidden="true"></span> Foo
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="@($"/some-page/{Slug}/bar")">
<span class="oi oi-home" aria-hidden="true"></span> Bar
</NavLink>
</li>
</ul>
</div>
@code {
[Parameter]
public string Slug { get; set; }
bool collapseNavMenu = true;
string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
string setupUrl = string.Empty;
void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
You can make the RouteData available to all your components via CascadingParameters. In your App.razor, do this:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<CascadingValue Value="@routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MyLayout)" />
</CascadingValue>
</Found>
</Router>
Then, in all components where you need the RouteData, simply add:
@code
{
[CascadingParameter]
RouteData RouteData { get; set; }
}
The value will be automatically populated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With