Search code examples
c#asp.netblazorblazor-webassembly

Modifiy Blazor Cascading Value between Child Components


I'm new to Blazor and trying to work through some of the basics of how to construct my components and pages. Right now I have a MainLayout that looks like this:

<CascadingValue Value="@layoutVals" Name="LayoutVals">
    <Leftbar />
    <div class="content-page">
        <Topbar />
        <div class="content">
            <div class="container-fluid">
                @Body
            </div>
        </div>
    </div>
</CascadingValue>
@code {
    public LayoutValues layoutVals = new LayoutValues()
    {
        Title = "Dashboard",
        Breadcrumbs = new Dictionary<string, string>()
        {
                { "Dashboard", "/" }
            }
    };
}

I'm trying to use the cascading value to allow a child page/component to overwrite values in the Topbar component (i.e. page title, breadcrumb, some other display values that I want consistent across views but dynamically replaced based on the page).

I'm able to access the object in the Topbar component and they are set properly based on what they're initialized to in MainLayout (shown above), and I'm able to override them within that component. However, if I set them in a page the change doesn't seem to make it's way up and then back down the chain to the Topbar component where I want them displayed.

I'm sure I could eliminate the Topbar component and inline everything in my MainLayout but I'd prefer to keep the code clean and separate if it's possible.


Solution

  • The problem you are facing is, that the <Topbar /> is not re-rendered after the value has changed in some component down below the tree (page, etx). You have to tell the Topbar that to render again:

    public class LayoutValues
        {
            public string Title {get;set;}
            public Action ValuesChanged; 
        }
    

    Subscribe to ValuesChanged in Topbar:

    protected override void OnInitialized()
    {
       LayoutVals.ValuesChanged += () => StateHasChanged();
      //dont forget to unsubscribe somewhere
    }
    [CascadingParameter] public LayoutValues LayoutVals {get;set;}
    

    And call it whenever you change the value (or you can do this in setter of LayoutValues):

    //some page
    private void ButtonClicked()
    {
     LayoutVals.Title="Changed title";
     LayoutVals.ValuesChanged.Invoke();
    
    }
    [CascadingParameter] public LayoutValues LayoutVals {get;set;}
    

    Working demo.

    This solution has a performance advantage - the app doesn't have to re-render the whole tree, just the Topbar has called the StateHasChanged.