Search code examples
eventscallbackcomponentsblazorcommunication

Blazor components: How to communicate from grandchild to child to parent or grandchild to parent


In my Blazor server app, I have a page called Home that is basically a search page. On the Home page (the "parent") there is a form with controls to filter the data stored in a backend database. Each row of the search results has an Edit button that displays a bootstrap dialog allowing one to edit the data. After the Save button on the UpdateDocumentNumber (grandchild) component is clicked I want to refresh the search results in the Home page (parent).

  • Parent: Home (page) with search results grid; embeds a DisplayDocumentNumber component for each item
  • Child: DisplayDocumentNumber component, which has its own (child) UpdateDocumentNumber component
  • Grandchild: UpdateDocumentNumber component - bootstrap dialog

My understanding is I can use Event Callback to do this; however, while I can raise/invoke the Event Callback from grandchild to child, I cannot seem to then inform the parent from the child.

Some more details...

As the Home ("parent" page) iterates over the returned list of items it inserts a <DisplayDocumentNumber> component (the "child"), and then the <DisplayDocumentNumber> component has a component to reference the Edit dialog. Here's the Home page iterating over the search results:

<tbody>
    @foreach (var documentNumber in DocumentNumbers)
        {
            <DisplayDocumentNumber DocumentNumber="documentNumber" /> // DocumentNumber parameter in DisplayDocumentNumber receives the data from the documentNumber local var
        }
</tbody>

Here's the DisplayDocumentNumber component:

public partial class DisplayDocumentNumber : ComponentBase
{
    [Parameter]
    public DocumentNumberDto DocumentNumber { get; set; }
    [Parameter]
    public EventCallback<bool> OnDocumentNumberUpdatedEventCallback { get; set; }
}

Note the public EventCallback<bool> OnDocumentNumberUpdatedEventCallback { get; set; }. This works properly from grandchild to child.

Inside the DisplayDocumentNumber.razor component is the row that gets rendered for each document number in the search results, including an Edit button that has a DOM event to show the bootstrap dialog. And, finally, as mentioned above, there is the <UpdateDocumentNumberDialog> component I.e.

<tr>
    <td>@DocumentNumber.Column1Name</td>
    <td>@DocumentNumber.Column2Name</td>
    ...etc
    <td><button class="btn btn-primary table-btn" @onclick="@(() => ShowEditDocumentNumberDialog(DocumentNumber))">Edit</button></td>
    <UpdateDocumentNumberDialog @ref="UpdateDocNumDialog" DocumentNumberForUpdating="DocumentNumber" DocumentNumberUpdatedEventCallback="@UpdateDocumentNumberDialog_OnDialogClose"></UpdateDocumentNumberDialog>
</tr>

If Event Callbacks only work from a grandchild to child or child to parent, do I have to create some kind of state container as described by Chris Sainty (https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/)? Or, am I missing something about Event Callbacks? I've tried to eliminate the child component, but failed because the Edit button always showed the last item iterated in the grid.


Solution

  • I can think of a few options. One is to give the grandchild access to the main page. This does not require an event at all:

    Parent.razor

    <CascadingValue Value="this">
        Body of page, somewhere in there including <Grandchild/>
    </CascadingValue>
    
    @code{
        async Task DoSomething(){
        }
    }
    

    Grandchild.razor

    @code {
        [CascadingParameter]
        public Parent MainPage {get; set;} // Or whatever your main page is called
    
        async Task StartSomething (){
            if (MainPage is not null) MainPage.DoSomething();
        }
    }