Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async event handler support #16237

Closed
SteveSandersonMS opened this issue Apr 9, 2018 · 8 comments
Closed

Add async event handler support #16237

SteveSandersonMS opened this issue Apr 9, 2018 · 8 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components

Comments

@SteveSandersonMS
Copy link
Member

Now we support oneventname="@MyMethod", we should create some built-in way to deal with MyMethod returning Task and having the renderer auto-rerender after that task completes.

Not sure whether this requires a syntax like oneventname-async or whether we can do it with oneventname dynamically based on the assigned value type.

@SteveSandersonMS
Copy link
Member Author

@rynowak Looking for your opinion on how this would fit best with dotnet/blazor#516.

@rynowak
Copy link
Member

rynowak commented Apr 9, 2018

My guess is that we could handle this with overloading in the runtime, which means that we wouldn't require different event names.

We could do different event names of course, if we wanted to.

We could test this theory by adding and additional overload to RenderTreeBuilder that accepts Func<UIEventArgs, Task> and see if this 'just works' (compiles) for some arbitrary event.

@rynowak
Copy link
Member

rynowak commented Apr 11, 2018

I suspect also that we'll want to require C# 7.3 for this. dotnet/csharplang#98
is really valuable for us because method group->delegate conversion is so idiomatic for Blazor.

The difference is that in C# 7.2:

public void AddAttribute(..., Action) { .. }
public void AddAttribute(..., Func<Task>) { .. }

public void Foo() { .. }
builder.AddAttribute(Foo);

This is considered an ambiguity in C#7.2 but not in C# 7.3.

Any concerns with this plan?

@chassq
Copy link
Contributor

chassq commented Apr 13, 2018

Please keep the eventname the same if possible is my $.02
oneventname="@Mymethod" --calling sync
-OR-
oneventname="@await MyMethod" --calling async

@Daddoon
Copy link

Daddoon commented Apr 19, 2018

Hello !

Any news on this subject ?
My current "workaround" for the moment is to do something like this:

@using Allogaia.AppResource.Properties
@using Allogaia.Web.Client.Helpers
@using Daddoon.Blazor.Helpers
@if (SignInManager.IsSignedIn())
{
    <ul class="nav navbar-nav navbar-top-links navbar-right">
        <li>
            <a>
                <!-- TODO: show static svg or profile image of current user -->
                @*@Html.RenderSVG("~/Images/resources/svg/user-circle.svg", "user-profile-image")*@
                @UserManager.GetUserFirstName()
            </a>
        </li>
        <li>
            <a onclick="@LogoutUser">@ResourceHelper.Get(nameof(Resources.LogOut))</a>
        </li>
    </ul>
}
else
{
    <ul class="nav navbar-top-links navbar-right">
        <li><a>@ResourceHelper.Get(nameof(@Resources.SignUp))</a></li>
        <li><a>@ResourceHelper.Get(nameof(@Resources.LogIn))</a></li>
    </ul>
}

@functions {

    private void LogoutUser(UIMouseEventArgs e)
    {
        Browser.SetTimeout(async delegate ()
        {
            bool success = await SignInManager.LogoutAsync();
            if (success == false)
            {
                JConsole.AlertDangerAnErrorOccured();
            }
        }, 10);
    }
} 

In order to have an async delegate called from my synchronous event, as my dispatched task with my SetTimeout helper is only waiting an Action type as delegate.

Otherwise do i missing something on the attribute binding for an async event ?

@rynowak
Copy link
Member

rynowak commented Apr 19, 2018

We're planning to do this for 0.3.0.

If you want a slightly less gross workaround, you can use async void.

private async void LogoutUser(UIMouseEventArgs e)
{
    await Task.Delay(10);
    bool success = await SignInManager.LogoutAsync();

    ...
    StateHasChanged();
}

The only thing that's missing is that you have to call StateHasChanged() yourself when you want to rerender - usually at the end

@Daddoon
Copy link

Daddoon commented Apr 19, 2018

Perfect !

Thanks a lot @rynowak !

@rynowak
Copy link
Member

rynowak commented Apr 26, 2018

This feature has been merged for 0.3.0 to the dev branch e5fd24b

Important - as of 0.3.0 we will require the use of C# 7.3. Using async event handlers smoothly will require some changes that have been made to overload resolution in C# 7.3. You can find their instructions for setting the C# language version in their documentation here. Note that you may need to restart visual studio if you change the language version for a project.


Async event handlers work a lot like regular event handlers except... well they are async.

Updating @Daddoon 's example:

...
        <li>
            <a onclick="@LogoutUser">@ResourceHelper.Get(nameof(Resources.LogOut))</a>
        </li>
...
@functions {

    private async Task LogoutUser(UIMouseEventArgs e)
    {
        await Task.Delay(10);

        // The runtime will call StateHasChanged the first time you 'await'

         bool success = await SignInManager.LogoutAsync();
         if (success == false)
         {
             JConsole.AlertDangerAnErrorOccured();
         }

        // The runtime will call StateHasChanged again when your task completes
    }
} 

@rynowak rynowak closed this as completed Apr 26, 2018
@mkArtakMSFT mkArtakMSFT transferred this issue from dotnet/blazor Oct 27, 2019
@mkArtakMSFT mkArtakMSFT added the area-blazor Includes: Blazor, Razor Components label Oct 27, 2019
@dotnet dotnet locked as resolved and limited conversation to collaborators Dec 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

No branches or pull requests

5 participants