42

I created a "Razor Components" project. I am trying to execute an asynchronous method when pressing a button, but could not figure out the syntax yet.

This is my Index.razor:

@page "/"
@inject GenericRepository<Person> PersonRepository 

@foreach (var person in persons)
{
    <button onclick="@(() => Delete(person.Id))">❌</button>
}

@functions 
{
    async void Delete(Guid personId)
    {
        await this.PersonRepository.Delete(personId);
    }
}

When I click the button, nothing happens. I tried various return types (e.g. Task) and stuff, but cannot figure out how to make it work. Please let me know if I need to provide more information.

Every documentation / tutorial only just works with non-async void calls on button click.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
ˈvɔlə
  • 9,204
  • 10
  • 63
  • 89

2 Answers2

61

You need to call the Delete method properly and make it return Task instead of void:

<button onclick="@(async () => await Delete(person.Id))">❌</button>

@functions {

    // ...

    async Task Delete(Guid personId)
    {
        await this.PersonRepository.Delete(personId);
    }
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • 15
    I wonder why everyone creates unnecessary async state machines (on anonymous methods). Why not just `onclick="@(e => Delete(person.Id))"`? Btw: `DeleteAsync` would not violate naming guidelines. – springy76 Apr 11 '19 at 13:30
  • 2
    Only trouble with this is that it does NOT call the method asynchronously. long tasks hang up the main thread :(. – Squibly Jun 30 '21 at 08:12
  • 2
    Do you mind adding some information to this post about the [newer advice for async calls](https://stackoverflow.com/a/60488286/1043380)? I keep getting this post as the first search result and keep forgetting there's a better way. – gunr2171 May 28 '22 at 03:36
  • @gunr2171 Been a while since I did any Blazor, but I think this advice is still valid. If you are passing in a parameter, you still need to use lambda syntax, though I think you can drop the `async` from the front if you like. – DavidG May 28 '22 at 16:16
  • I'm not clear what bit of code Squibly is refering to, is it the comment above or the code in the answer Would the async () => await Delete(...) block the main thread, or is it the e => Delete(...) that does that? – andrew pate Dec 08 '22 at 10:59
  • Answering my own comment... having read (https://www.codeproject.com/Articles/5276310/Async-Programming-in-Blazor) It looks like doing the async ()=> is just wrapping one task in another. Its just important that Delete returns a Task. – andrew pate Dec 08 '22 at 11:09
20

@WoIIe, 1. The purpose of using a lambda expression as a value for the onclick attribute is so that you can pass a value to the Delete method. If you have already defined a person object in your code, you don't have to use a lambda expression. Just do this: onclick = "@Delete", and access person.Id from the Delete method.

  1. Did you click the button a second time? I believe that this code: await this.PersonRepository.Delete(personId); did execute, but you've seen no response on the GUI because the use of void, which is not recommended, requires you to call StateHasChanged(); manually to re-render. Note that StateHasChanged() has already been automatically called once when your method "ended", but since you're returning void and not Task, you should call StateHasChanged() once again to see the changes. But don't do it. See the answer by DavidG how to code properly.

This is also how you can code:

<button onclick="@Delete">Delete Me</button>

@functions {

    Person person = new Person();
    //....
    async Task Delete()
    {
        await this.PersonRepository.Delete(person.Id);
    }
}

More code as per request...

 foreach(var person in people)
    {
        <button onclick="@(async () => await Delete(person.Id))">Delete</button>
    }

@functions {
  // Get a list of People.
  List<Person> People ;

protected override async Task OnParametersSetAsync()
{
    People = await this.PersonRepository.getAll();
}

async Task Delete(Guid personId)
{
     await this.PersonRepository.Delete(personId);
}
}

Note: If you still have not solved your problems, show all your code

David Bond
  • 149
  • 1
  • 9
enet
  • 41,195
  • 5
  • 76
  • 113
  • 1
    Thanks, but the button is inside a `foreach(var person in perons)` loop. Could you provide some code how you would realize that? Thanks for your clarification in your second point. Ill try that out later :) – ˈvɔlə Apr 03 '19 at 18:07