'Call method in MainLayout from a page component in Blazor
I have a Blazor app with a MainLayout
page, which has a @Body
to load the actual page content.
In my case Index.razor
is loaded inside the MainLayout
page.
Is there a way to call a method from the child page (Index.razor) that lives in the parent page; MainLayout.razor
?
Example:
MainLayout.razor
<div class="content">
<ul class="menu">
<li>menu item 1</li>
</ul>
@Body
</div>
@code
{
public async Task Test()
{
await JsRuntime.InvokeAsync<object>("console.log", "test parent");
}
}
Index.razor
<h1>This is the index page</h1>
<button @onclick="(async () => await btn_clicked())">Call parent method</button>
@code
{
// Call method in MainLayout
public async Task btn_clicked()
{
await parent.Test();
}
}
Solution 1:[1]
You can do this with the combination of cascading values and EventCallback
.
First, create an event call back for your Test
. To do so, add the following code in your MainLayout.razor.
EventCallback btn_clicked => EventCallback.Factory.Create(this, Test);
Or, to make sure you only create this object once, you can use the following:
EventCallback _btn_clicked = EventCallback.Empty;
EventCallback btn_clicked {
get {
if (_btn_clicked.Equals(EventCallback.Empty))
_btn_clicked = EventCallback.Factory.Create(this, Test);
return _btn_clicked;
}
}
Next, make sure you cascade this event callback down your body.
<CascadingValue Value=btn_clicked >
@Body
</CascadingValue>
Now, in your Index.razor code, set the property:
[CascadingParameter]
public EventCallback btn_clicked { get; set; }
Solution 2:[2]
Thanks. The first answer was exactly what I needed, though I needed to pass in a parameter, so it's the same as laid out, except:
In MainLayout.razor:
public EventCallback<string> EventName => EventCallback.Factory.Create<string>(this, MethodName);
In Index.razor:
[CascadingParameter]
protected EventCallback<string> EventName { get; set; }
To call the method:
EventName.InvokeAsync("Name");
Solution 3:[3]
The callback methods work, but are still fairly limited, the approach I took was to use "Mesaging Center" that has been cloned from Xamarin.Forms...
https://github.com/aksoftware98/blazor-utilities
https://www.nuget.org/packages/AKSoftware.Blazor.Utilities/
It's very simple, flexible, and behaves much more like a pubsub messaging system across components interanally which has many benefits.
As a result you can "broadcast" events to individual or multiple components regardless of nesting state and location - super flexible and useful. Taken from their docs...
Place a using in your _imports.razor
@using AKSoftware.Blazor.Utilities
Then put a subscriber in the MainLayout.razor
public void SubscribeToMessage()
{
MessagingCenter.Subscribe<Component1, string>(this, "greeting_message",
(sender, value) =>
{
// Do actions against the value
// If the value is updating the component make sure to call
string greeting = $"Welcome {value}";
StateHasChanged(); // To update the state of the component
});
}
And a publisher in any of the componets you want
public void SendMessage()
{
string valueToSend = "Hi from Component 1";
MessagingCenter.Send(this, "greeting_message", valueToSend);
}
You can put subscribers and senders in any combination of components. I've been making a project where realtime date was updating multiple componenets at the same time, this was a great solution that allowed me to put all my hub connetion logic in MainLayout.razor, and then call and respond to that across all compoents as needed, prvented multiple hub connections being created, and works well in tandem with cascading states.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Neville Nazerane |
Solution 2 | James Thomas |
Solution 3 | user520100 |