How to get return value from EventCallback in blazor?
Asked Answered
N

4

14

My situation is this: I'm trying to implement and Autocomplete.

The Autocomplete will have a Parameter that will receive a string and return a IEnumerable<TValue>.

Here is an example of what I'm trying to do

Autocomplete.razor

@code {
    [Parameter]
    public SOME_TYPE GetItems { get; set; }

    async void Foo(){
        IEnumerable<TValue> items = await GetItems(SomeString);
        // do something with items
    } 
}

ParentComponent.razor

<Autocomplete TValue="SomeEntity"
              GetItems="@GetItems" />

@code {        
    SOME_TYPE GetItems(string name) {
        IEnumerable<SomeEntity> entity = await GetEntitys(name);
        return entity;
    } 
}

The problem is that I don't know what to put in SOME_TYPE. Should I use EventCallback? Action? What should I use?

I tried using EventCallback but looks like I can't get a return value from EventCallback? I have no idea.

Nitrate answered 21/4, 2020 at 14:12 Comment(0)
N
15

I just find out how to do it, I should use Func<string, Task<IEnumerable<TValue>>>.

[Parameter]
public Func<string, Task<IEnumerable<TValue>>> GetItems { get; set; }

And

public async Task<IEnumerable<Employee>> GetItems(string name) {
    IEnumerable<SomeEntity> entity = await GetEntitys(name);
    return entity;
} 
Nitrate answered 22/4, 2020 at 11:24 Comment(1)
but doing so you loose automatic component refresh after each event handler call. You have to call StateHasChanged by yourselfAciculum
S
10

You can also use argument of EventCallback for that:

public class CancelArg
{
    public bool Value { get; set; }
}

[Parameter]
public EventCallback<CancelArg> OnFoo { get; set; }

async Task CallerInChild()
{
    var cancel = new CancelArg();
    await OnFoo.InvokeAsync(cancel);
    if (cancel.Value)
    {
        // cancelled ...
    }
}

void HandlerInParent(CancelArg cancel)
{
    cancel.Value = true;
}
Surly answered 8/1, 2021 at 21:13 Comment(2)
Would be good to also show how this is implemented in the mark up for Blazor noobsMaidinwaiting
How would you test this with f.x bunitHarv
K
6

In a child component:

<div class="form-group">
    <label for="ddSetStatus">Status</label>
    <InputSelect class="form-control" id="ddSetStatus" @bind-Value="@Status" @oninput="@((e) => ChangedSelection(e))">
        <option value="1">Draft </option>
        <option value="2">Published </option>
        <option value="3">Archived </option>
   </InputSelect>
</div>

    @code
    {
    [Parameter] public EventCallback<int> OnStatusSelected { get; set; }

    [Parameter] public int Status { get; set; }

    private async Task ChangedSelection(ChangeEventArgs args)
    {
        await OnStatusSelected.InvokeAsync(int.Parse(args.Value.ToString()));
    }
    }

and then consuming the selected value in a parent page or component:

create a component (called DocStatus in this example) and define the event handling method

in markup area:


 <DocStatus Status="@Status" OnStatusSelected="StatusSelectionHandler"/>

in the code area:


 private void StatusSelectionHandler(int newValue)
 {
     Status = newValue;   
 }

Kidney answered 16/10, 2021 at 18:51 Comment(0)
K
0

Based on @kzaw response I have created an extension method:

public static class EventCallbackExtensions
{
    public static async Task<TOutput?> InvokeReturnAsync<TInput, TOutput>(this EventCallback<EventCallbackReturnParameter<TInput, TOutput?>> callback, TInput input, TOutput? output = default)
    {
        var callbackReturn = new EventCallbackReturnParameter<TInput, TOutput?>()
        {
            Value = input,
            OutputValue = output
        };

        await callback.InvokeAsync(callbackReturn);

        return callbackReturn.OutputValue;
    }
}

which uses generic class for storing input and output like:

public sealed class EventCallbackReturnParameter<TInput, TOutput>
{
    public TInput? Value { get; set; }
    public TOutput? OutputValue { get; set; }

    public EventCallbackReturnParameter() 
    {
    }

    public EventCallbackReturnParameter(TOutput outputValue) 
    {
        OutputValue = outputValue;
    }

    public EventCallbackReturnParameter(TInput value, TOutput outputValue) 
    {
        Value = value;
        OutputValue = outputValue;
    }
}

which can be used like:

ChildComponent.razor

<span @onclick="async ()=> await OnSelect()">Item to select</span>

@code
{
    [Parameter] public EventCallback<EventCallbackReturnParameter<string, string>> OnItemSelected { get; set; }

    private async Task OnSelect() 
    {
        var outputText = await OnItemSelected.InvokeReturnAsync("selected item");
    }
}

ParentComponent.razor

<ListRow OnSelected="OnSelected" />

@code
{
    private async Task OnSelected(EventCallbackReturnParameter<string, string> callbackParameter) 
    {
        callbackParameter.OutputValue = "Output value";
    }
}
Kaylakayle answered 26/5, 2023 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.