Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 :-/
    }
}
like image 360
Aaron Hudon Avatar asked Sep 05 '19 18:09

Aaron Hudon


People also ask

What is a Blazor layout?

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.

What's wrong with the cascading parameter approach in Blazor?

The cascading parameter approach also has some underlying issues: Blazor WASM - Navigation away from Layout using RouteData creates duplicate components

How to change page behaviour based on url in Blazor?

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.

How to parse out query string parameters in Blazor?

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!


2 Answers

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;
    }
}
like image 158
Aaron Hudon Avatar answered Oct 17 '22 10:10

Aaron Hudon


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.

like image 39
tocqueville Avatar answered Oct 17 '22 11:10

tocqueville