Aspnetcore: "There is no event handler associated with this event" when an element is focused out

Created on 13 Oct 2020  路  13Comments  路  Source: dotnet/aspnetcore

Describe the bug

An error is thrown:

Error: System.ArgumentException: There is no event handler associated with this event. EventId: '3'. (Parameter 'eventHandlerId')

when an element with focusout handler is removed from the DOM in a blazor wasm app (both 3.1 and 5.0).

To Reproduce

  1. Clone the https://github.com/Stamo-Gochev/blazor-net5-wasm-focusout repository
  2. Set the TestNet5Wasm.Server as a startup project.
    > Note: In order to test the 3.1 version of wasm, use the TestNet31Wasm.Server
  3. Run the app (Ctrl + F5)
  4. On the Index.razor page, click on item 2 that has @onfocusout handler

Note: The main logic is part of the Index.razor page only and the script.js file.

Exceptions (if any)

Error: System.ArgumentException: There is no event handler associated with this event. EventId: '3'. (Parameter 'eventHandlerId')

Further technical details

  • ASP.NET Core version 3.1, 5.0 RC1
  • Include the output of dotnet --info
  • The IDE (VS / VS Code/ VS4Mac) you're running on, and it's version

Additional context

A similar problem is marked to be fixed in multiple cases like:
https://github.com/dotnet/aspnetcore/issues/17926
https://github.com/dotnet/aspnetcore/issues/21241
https://github.com/dotnet/aspnetcore/issues/21366
https://github.com/dotnet/aspnetcore/issues/21874#issuecomment-632814894

but the problem is still reproducible in both the 3.1 and 5.0 versions.

affected-medium area-blazor blazor-wasm bug investigate severity-major

Most helpful comment

I did some investigation on this today, and confirmed it is a current bug. The repro can be simplified to:

@if (showButtonThatDisappearsWhenClicked)
{
    <button @onfocusout="HandleFocusOut" @onclick="HandleClick">Click me</button>
}

@code {
    bool showButtonThatDisappearsWhenClicked = true;

    void HandleClick()
    {
        Console.WriteLine("click");
        showButtonThatDisappearsWhenClicked = false;
    }

    void HandleFocusOut()
    {
        Console.WriteLine("focusout");
    }
}

This works OK on Blazor Server but throws on Blazor WebAssembly. On wasm, the sequence of events is:

  • JS: Notify click event

    • .NET: Track that an event handler is in progress
    • .NET: Notify new renderbatch, which includes removing element and disposing the focusout event handler
    • JS: Lock the heap
    • JS: Upon removing element from DOM, 'focusout' JS event fires, so wants to notify 'focusout' to .NET

      • JS: Can't issue notification synchronously because heap is locked. Defer it.

    • JS: Finish applying renderbatch and unlock heap, synchronously processing deferred calls

      • JS: Now we can dispatch the notification for 'focusout' to .NET [*]

      • .NET: I'm already handling an event, so defer this until later

    • JS: Notify .NET that renderbatch finished being applied

      • .NET: Release the event 'focusout' handler

      • .NET: Track that event handling is finished and we're ready for more events

  • Next clock tick:

    • .NET: OK I'm ready to run the deferred event handler from earlier
    • .NET: That event handler was already released - CRASH

Possible resolution:

  • When JS is trying to dispatch and event but has to defer it [*], check the event handler is still known
    inside the deferral callback, not outside it (as we do today). Then in this case we'd never dispatch the 'focusout' event to .NET,
    so there'd be no exception, but also no call to the focusout handler.

    • But that's inconsistent with server behavior. The server would queue up the 'focusout' event notification and

      deliver it before the first 'batch complete' confirmation, and hence the 'focusout' event would fire.

    • To make Wasm consistent with server, we could change the .NET side code so that, when being notified that

      a renderbatch finished application, before releasing its event handlers we execute any deferred event notifications

All 13 comments

Thanks for contacting us.
We're moving this issue to the Next sprint planning milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

I did some investigation on this today, and confirmed it is a current bug. The repro can be simplified to:

@if (showButtonThatDisappearsWhenClicked)
{
    <button @onfocusout="HandleFocusOut" @onclick="HandleClick">Click me</button>
}

@code {
    bool showButtonThatDisappearsWhenClicked = true;

    void HandleClick()
    {
        Console.WriteLine("click");
        showButtonThatDisappearsWhenClicked = false;
    }

    void HandleFocusOut()
    {
        Console.WriteLine("focusout");
    }
}

This works OK on Blazor Server but throws on Blazor WebAssembly. On wasm, the sequence of events is:

  • JS: Notify click event

    • .NET: Track that an event handler is in progress
    • .NET: Notify new renderbatch, which includes removing element and disposing the focusout event handler
    • JS: Lock the heap
    • JS: Upon removing element from DOM, 'focusout' JS event fires, so wants to notify 'focusout' to .NET

      • JS: Can't issue notification synchronously because heap is locked. Defer it.

    • JS: Finish applying renderbatch and unlock heap, synchronously processing deferred calls

      • JS: Now we can dispatch the notification for 'focusout' to .NET [*]

      • .NET: I'm already handling an event, so defer this until later

    • JS: Notify .NET that renderbatch finished being applied

      • .NET: Release the event 'focusout' handler

      • .NET: Track that event handling is finished and we're ready for more events

  • Next clock tick:

    • .NET: OK I'm ready to run the deferred event handler from earlier
    • .NET: That event handler was already released - CRASH

Possible resolution:

  • When JS is trying to dispatch and event but has to defer it [*], check the event handler is still known
    inside the deferral callback, not outside it (as we do today). Then in this case we'd never dispatch the 'focusout' event to .NET,
    so there'd be no exception, but also no call to the focusout handler.

    • But that's inconsistent with server behavior. The server would queue up the 'focusout' event notification and

      deliver it before the first 'batch complete' confirmation, and hence the 'focusout' event would fire.

    • To make Wasm consistent with server, we could change the .NET side code so that, when being notified that

      a renderbatch finished application, before releasing its event handlers we execute any deferred event notifications

With .NET5 RC2 it seems to be working even with Blazor WebAssembly. Did not get any error. But VS 2019 Intelligense does not show this event handler only onfucus:

image

@majorimi It's not clear to me that what you're posting here is the same thing as this issue. Instead of posting here, could you please file a new issue giving a clear description of what you're doing (since it's likely totally different)?

@SteveSandersonMS: I was trying out @onfocusout event with a very similar code as your example above. Run it in a Blazor WebAssembly (not server side) project and I received the events without exceptions. So just confirmed bug was fixed...

Also my point was if this event is supported by Blazor and works perfectly, why Visual Studio 2019 Intelligense is not offering it when I'm typing focus keyword?
As you can see on that screenshot Intelligense has 1 result for @onfocus however it should match with 2 items: @onfocus and @onfocusout as well.

Sure lets post it to a new issue, however not sure if is it belongs to this repo or should be posted for VS team? Thanks.

Sure lets post it to a new issue

Yes please, in this repo.

Just a note that this breaks data binding scenarios:

  1. User enters data into text field
  2. User navigates away, causing field to be destroyed by DOM
  3. Event fires, causing bug. User data is lost.

I haven't found a reliable workaround. Changing the bind:event to oninput works, but only when binding is appropriate for that event. A workaround for this would be great.

I am also facing the same script issue.

Any workarounds for resolving this ?

@RobertBouillon @Indrajith-Sync If you could each post separate issues with your own minimal repro steps, we can figure out whether it鈥檚 the same issue or not. Thanks!

@SteveSandersonMS Can you clarify if a potential fix will be backported to the 3.x branch or will it be part of the upcoming 5.0.1 release only? I need to support both 3.x and 5.0, so this information will be helpful to me beforehand.

We don't have a decision about that yet, however as a caution, please note that 3.2.0 is not an LTS release and hence doesn't come with support beyond (I think) February 2021. Developers using Blazor WebAssembly should upgrade to 5.0 before February 2021.

You are probably referring to the 6.0-preview1 milestone targeting February 10, 2021 for an end date for wasm 3.2.0, but are any fixes for wasm going to be part of any of the 3.1.x releases? As far as I understand, Blazor WebAssembly 3.2.0 is not considered to be connected with the 3.1 LTS (ending on December 3, 2022), but I cannot find any official statement about that, am I correct?

You can find out more about support policy and roadmap at https://dotnet.microsoft.com/platform/support/policy/dotnet-core. You are correct that 3.2.x is not part of the 3.1.x LTS release (in fact, that's why we gave it the 3.2.0 version originally).

Was this page helpful?
0 / 5 - 0 ratings