2

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.

4
  • CascadingValues are exactly that, cascading. You can define a callback (eg; OnDocumentNumberUpdatedEventCallback) on the parent, and cascade the parent down to the grandchild components, where the grandchild component can invoke the callback on the parent. Commented Jul 9, 2021 at 3:44
  • So far, I haven't gotten this to work, so I am missing something. Continuing this effort though. Thanks!
    – LWC433
    Commented Jul 9, 2021 at 18:43
  • Your explanation is good, but for me to get this to work I needed the <CascadingValue Value="this">, specifically the Value="this" part provided by @Bennyboy1973. I have more to learn about using "this". Thanks again!
    – LWC433
    Commented Jul 12, 2021 at 14:51
  • Passing Value="this" is also covered in the blog post from Chris Sainty that you posted yourself. Might be worth a second read-through. Commented Jul 12, 2021 at 16:42

2 Answers 2

5

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();
    }
}
2
  • If you don't want to use a service this is by far the easiest. Commented Jul 10, 2021 at 14:05
  • I had to add StateHasChanged() at the end of the MainPage.DoSomething() method to manually trigger a refrsh to the UI after that method was called from the grandchild component. This wasnt required when triggered from mainpage itself. Commented Apr 17, 2022 at 0:01
1

I include the following parameter in the Child and Grandchild components:

 [Parameter]
    public Action MyDataHasChanged {get;set;}

Further, inside my Grandchild component:

@code
{
  public void RefreshMySearchResultsInParent()
  {
      MyDataHasChanged?.Invoke();
  }
}

Inside my Child component:

<Grandchild @MyDataHasChanged="MyDataHasChanged"></Grandhild>

Inside my Parent Component:

<Child @MyDataHasChanged="RefreshMySearchResults"></Child>



 @code
    {
    
    public void RefreshMySearchResults()
    {
       //Do this...
    }

}
4
  • 2
    I do it almost this way except I put the event in a class that is injected in any page that needs it. Commented Jul 9, 2021 at 15:55
  • Brilliant idea @danielSnyder - This will also make maintenance much easier 👍 Commented Jul 10, 2021 at 9:18
  • 1
    Thanks. I call the overall pattern SWIMMER I is for injection of the repository, events and models and templates. (The content of the template is from the db) It also allows granular control of page change notification. The use is to create a tool to develop Blazor single page applications. I’m going to build it someday Commented Jul 11, 2021 at 17:30
  • Also I am now banned from asking questions, so if I was brilliant, I guess I won't be anymore. Commented Aug 22, 2021 at 18:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.