0
votes

I'm hoping someone can help as I'm new to Blazor.

I've started a new Blazor project and am trying to add contextual buttons in the top bar that can be changed by / based on the child component (when you navigate to page 1 it can show different things to when you navigate to page 2)

enter image description here

This is my approach so far:

TopBar.razor - A template for the top bar with render fragment so I can render different stuff

@inject Learning.Data.TopBarContext TopBarContext

<div class="col-sm-6">@Context</div>
<span class="col-sm-2">@Text1</span>
<span class="col-sm-2">@Text2</span>

@code {
    [Parameter]
    private RenderFragment Context { get; set; }

    [Parameter]
    public string Text1 { get; set; } = "Not Set";

    [Parameter]
    public string Text2 { get; set; } = "Not Set";    
}

MainLayout.razor - I use TopBar and cascade it down to all the pages

@inherits LayoutComponentBase

<div class="sidebar">
    <NavMenu />
</div>

<div class="main">
    <div class="top-row px-4 auth">
        <TopBar />
    </div>

    <div class="content px-4">
        <CascadingValue Value="TopBar" Name="TopBar">
            @Body
        </CascadingValue>        
    </div>
</div>

@code {
    private TopBar TopBar;
}

Now I have to two page components:

Page1.razor - When navigated to will render 3 buttons in top bar

@page "/page1"

<button>One</button>
<button>Two</button>
<button>Three</button>

@code {

}

Page2.razor - When navigated to will render 2 buttons in top bar

@page "/page2"

<button>One</button>
<button>Two</button>

@code {

}

I know I can use the code below to pick up the cascaded value:

[CascadingParameter(Name = "TopBar")]
    protected string TopBar { get; set; }

But not sure how to proceed, I was hoping for something like cascading the whole component down so that in page1 I could do something flexible like:

<TopBar>
    <button>One</button>
    <button>Two</button>
    <button>Three</button>
</TopBar>

I may be going about this completely the wrong way (I did think creating a registered singleton might be a way to do it) but any clarification you could offer on how to accomplish this would be greatly appreciated!

Many Thanks

Nick

1

1 Answers

0
votes

Looking at what you have above, you are almost there. You can cascade the TopBar like you have it in your MainLayout, but the code section where you are picking it up is what is throwing you off. You have the cascaded value shown as a string property, but remember that Blazor components are classes that are compiled, so you need to list it as a class instead:

[CascadingParameter]
public TopBar Bar { get; set; }

That is how you will capture it in your components, and then you can access the public properties and methods to update them, using code like Bar.Context = // render fragment of your choice

This might get a bit tricky though as what you feed to the Context property could prove to be painful to build up, so it might be better to have your TopBar component render something itself based on a data structure that it holds instead. I've done something with a Dictionary before for links that worked well, so in your TopBar you would have a private property:

private Dictionary<string, string> LinkBodiesAndRefs { get; set; } = new Dictionary<string, string>();

This stored link text as the key and the target URL as the value for each record, and then to render them, something like the following:

TopBar.razor

@foreach (KeyValuePair<string, string> entry in LinkBodiesAndRefs)
{
    <a href="@entry.Value">@entry.Key</a>
}

Then in the @code block of the TopBar, I added a few methods to interact with the dictionary:

TopBar.razor (continued)

@code {

    private Dictionary<string, string> LinkBodiesAndRefs { get; set; } = new Dictionary<string, string>();

    public void AddLink(string linkBody, string linkDestination)
    {
        LinkBodiesAndRefs.Add(linkBody, linkDestination);
        StateHasChanged();
    }

    public void RemoveLink(string linkBody)
    {
        LinkBodiesAndRefs.Remove(linkBody);
        StateHasChanged();
    }

    public void Clear()
    {
        LinkBodiesAndRefs = new Dictionary<string, string>();
        StateHasChanged();
    }
}

Then in your component that captures the cascaded TopBar, just use the methods to interact with the data structure as you would expect, whether it be a list or dictionary or whatever:

Bar.AddLink("Products", "/products");

Bar.RemoveLink("Products");

The same approach could be used with lists of objects if they need further interaction, and those could even be Razor Components and you could have a list of RenderFragment in your TopBar that gets rendered out if you wanted. The point is that the CascadingValue can provide a class that you can then define the interactivity with, and you can get up to all sorts of shenanigans to make cool things happen.

Hope this helps, and I don't have my IDE handy at the moment so there may be some syntax to work out that I missed in the above examples. Sorry in advance if that's the case.