2

I have a need to adjust a child component based on the result of EventCallback<T> async call, starting with reporting an error in validation summary. Yet I cannot seem to find a blazor-way to do this efficiently, it seems that communication is strictly one-way, from child to parent.

I read some SO answer that you can use Func<T1, T2> instead of EventCallback<T>, but that it has a serious downside of not calling StateHasChanged() when needed.

    [Parameter]
    public EventCallback<int> OnAccountEntered { get; set; }

    private async Task HandleValidSubmit()
    {
        try
        {
            DisableButton = true;
            ButtonText = "Please Wait, Validating Account Information";
            await OnAccountEntered.InvokeAsync(Model.AccountNumber ?? 0).ConfigureAwait(false);
            
            // here be dragons. how do I get the answer from parent?
        }
        finally
        {
            ButtonText = "Request";
            DisableButton = false;
        }
    }

What would be the proper way to assure this two-way communication between parent and child object?

mmix
  • 6,057
  • 3
  • 39
  • 65
  • https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-7.0 – maciek May 08 '23 at 11:46
  • Maybe create a method on your child component that can be called by the parent after the EventCallback. Another option is to add a parameter in your child component for the data you want. When this parameter is changed by the parent, it will automatically rerender the child component. – Axekan May 08 '23 at 12:40
  • What does `OnAccountEntered` in the parent actually do? Does it change the state of the parent? Is is just a call to get some data from somewhere else? On the information provided a `Func` is a perfectly good solution. The component will render after `HandleValidSubmit` completes. – MrC aka Shaun Curtis May 08 '23 at 15:03
  • "but that it has a serious downside of not calling StateHasChanged() when needed." That's not a downside: just a fact. If you're mutating the state of the component call `StateHasChanged` manually. It's the same with any standard event handler. – MrC aka Shaun Curtis May 08 '23 at 15:06
  • Why not use `Value`/`ValueChanged` properties in the Child Component and then use the `@bind-Value` feature in the parent markup? This will provide two-way communication for your property. – Valuator May 08 '23 at 15:40

1 Answers1

2

There's no "Serious Downside" or anything wrong with using a Func delegate. You have control over whether or not you refresh the parent component. Dragons [like the comment] are myths.

Here's a quick demo using a Func<Task, int>.

MyComponent

<div class="bg-light m-2 p-2">
    <h3>MyComponent</h3>
    <button class="btn btn-primary" @onclick=this.HandleValidSubmit>Update</button>
    <div class="bg-secondary text-white m-2 p-1">
        <pre>Counter: @counter </pre>
    </div>
</div>


@code {
    [Parameter] public Func<int, Task>? OnAccountEntered { get; set; }
    private int counter;

    private async Task HandleValidSubmit()
    {
        if (this.OnAccountEntered is not null)
        {
            // unless you're an async expert keep the await clean and simple
            await this.OnAccountEntered.Invoke(counter + 1);
            counter++;
            // put in a delay so you can see this component update afer the parent
            await Task.Delay(1000);
        }
    }
}

And Index

@page "/"

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<div class="bg-dark text-white m-2 p-1">
    <pre>Counter: @counter </pre>
</div>

<MyComponent OnAccountEntered=this.AccountChanged />

@code{
    private int counter;

    private async Task AccountChanged(int value)
    {
        counter = value;
        // pretend we're an async method getting data from some async source
        await Task.Yield();
        // call StateHasChanged if the process mutates the state of this component
        StateHasChanged();
    }
}
MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
  • Ok, in the end, I did it this way. Seemed more Blazor-y than using two way binding (since I have async processing in between). Thanks. Still wish there was a `EventCallback` option, due to the single subscriber semantics and embedding into Blazor pipeline. – mmix May 09 '23 at 19:31