Blazor: Access parameter from layout
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 :-/
}
}
Solution 1:
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.
Solution 2:
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;
}
}