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?
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();
}
}