0
votes

I have a component with one child component

<MyBtn Command="@ShowBusyCommand"></MyBtn>

@if(IsBusy)
{
<span>I'm busy</span>
}

@code{
 public bool IsBusy { get; set; }

 public ICommand ShowBusyCommand => RegisterCommand(() => ShowBusyCommand, CanExecute, ExecuteAsync);

 public async Task ExecuteAsync()
    {
        try
        {
            IsBusy = true;
            await Task.Delay(2000);
        }
        finally
        {
            IsBusy = false;
        }
    }
}

And MyBtn component

<button @onclick="@Execute">Show busy</button>

@code {
    [Parameter]
    public ICommand Command { get; set; }

    public void Execute()
    {
        Command.Execute(null);
    }
}

When I click on the button ExecuteAsync method is executed but the view does not change. I don't see my span with "I'm busy" text.

I've even tried creating an EventCallback in MyBtn

<button @onclick="@OnClickHandler">Show busy</button>

@code {
    [Parameter]
    public ICommand Command { get; set; }

    [Parameter] 
    public object Receiver { get; set; }

    public async Task OnClickHandler()
    {
        var onClick = EventCallback.Factory.Create(Receiver, () => Command.Execute(null));
        await onClick.InvokeAsync(null);
    }
}


  <MyBtn Command="@ShowBusyCommand" Receiver="this"></MyBtn>

but that did not help either.

I can't figure out why doesn't the parent state change when button in a component is clicked and how can I fix that? Except calling manually StateHasChanged on IsBusy property change.

If I change my parameter from ICommand to EventCallback in MyBtn component and pass ExecuteAsync method it works. Why? How can I make it work by passing ICommand as parameter and invoking Execute method?

EDIT: I just figured out if I remove lambda from EventCallback factory, it works but I don't know why

var onClick = EventCallback.Factory.Create(Receiver, Command.Execute);
1

1 Answers

3
votes

This is a working code of your question...

Index.razor

@page "/"

<MyBtn Func="@ShowBusyAsync"></MyBtn>

@if(IsBusy)
{
<span>I'm busy</span>
}

@code{
 public bool IsBusy { get; set; }

 public async Task ShowBusyAsync(int delay)
    {
        try
        {
            IsBusy = true;
            await Task.Delay(delay);
        }
        finally
        {
            IsBusy = false;
        }
    }
}

MyBtn.razor

<h3>MyBtn</h3>

<button @onclick="@ExecuteAsync">Show busy</button>

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

    public async void ExecuteAsync()
    {
        await Func.InvokeAsync(3000);
    }
    }

Note: You should use the EventCallback if you wish to re-render the parent component as the EventCallback creates a delegate whose Target property is set to the object that registered the event, that is, the parent component. If you don't want the parent component to be the Target of the event, use the Action or Func delegates instead.