0
votes

I have a side drawer in my MainLayout.razor component, which is used to set/change certain values which are then used throughout the app.

<CascadingValue Value="@appValues" Name="AppValue">
    <SideDrawer OnUpdate="@RefreshPage"></SideDrawer>
    <div class="content px-4">
        @Body
    </div>
</CascadingValue>

When I update the values in the SideDrawer, I do an EventCallback, through which I can update the variables which can then be used as cascading values throughout the page components.

@code{
    public AppValues appValues = new AppValues();
    protected void RefreshPage()
    {
        appValues.value1 = somevaluefromsidedrawer;
        appValues.value2 = someothervaluefromsidedrawer;
        StateHasChanged();
    }
}

And these cascading values get updated just fine in the page components. But the problem is, in the page components, there is a method (let's say LoadData()) in which certain datasets should be updated based on these cascading values.

@code{
    [CascadingParameter(Name = "AppValue")]
    protected AppValues appValues { get; set; }
    protected void RefreshCurrentPage()
    {
        LoadData(appValues.value1);
    }
}

Ideally, I would like to be able to call the RefreshCurrentPage() method in the page component from the RefreshPage() method in the MainLayout.razor component, so that all the datasets in the page component are refreshed based on the updated values.

Is it possible to do something like this?

1
You might check out this blog post which outlines a fairly simple way to trigger renders farther up the DOM tree. The second thing you might look at is using a service to trigger re-renders. I have an answer here that should get you pointed in the right direction.Nik P
Thanks @NikP. I'll start looking into yours and enet's answers and see which better suits my need. But they have been very helpful in learning new ways to do these things.Raj

1 Answers

1
votes

You can do that in various ways:

You can pass a reference to the MainLayout component to interested child components in the form of a CascadingValue vomponent, like this:

<CascadingValue Value="this" Name="TheMainLayout">
    
</CascadingValue>

And a child component should grab the reference like this:

@code{
    [CascadingParameter(Name = "TheMainLayout")]
    public MainLayout MainLayout { get; set; }
    
}

And add itself to a list of components defined in the MainLayout component:

 // Child component adds itself to the  MainLayout component
    protected override void OnInitialized()
    {
        MainLayout.AddPage(this);
    }

And in the MainLayout component you'll define a list of ComponentBase objects, and the AddPage method as follows:

public void AddPage(ComponentBase page)
{
    // Code to add the page component to the list
}

Now that the MainLayout holds references to components, it can directly call the RefreshCurrentPage defined in each added page components (I guess it can only be one as we are talking about routable components, right).

Note: The above was only the outline of a solution. You may extend it to provide an event that is raised each time there is a need to refresh the data, and each page component should subscribe to the event, which when raised, call the RefreshCurrentPage method. This is a better and more sophisticated way than calling the RefreshCurrentPage directly from the MainLayout.

You should provide code to remove the added component reference from the list of components, when those components are killed, etc.

Note that the CascadingValue for appValues is not needed any longer, as the child component hold a reference to the MainLayout and can directly access those values.

Note: All the above procedure can be implemented by a service class injected into the MainLayout component and its children.

So long, buddy...