8

I have a javascript event called Hello:

addEventListener('hello', function () {
  alert("event listener");
})

and, in another javascript function, I raise the event:

let event = new Event("hello", { bubbles: true });
document.dispatchEvent(event);

What I want to do now is let the event trigger in a javascript function. Blazor should listen to the event, not javascript calling a Blazor method.

Hope anyone can assist me.

Regards me,

5
  • What version of .NET are you using? Commented May 18, 2021 at 23:12
  • i'm using .Net 5.0 Commented May 18, 2021 at 23:27
  • I only asked because .NET 6 preview 2 introduces custom event types to Blazor. Until then, if you want to handle a custom event like yours, you would need to "trick" blazor by having your custom event handler dispatch a standard event that your are listening for, e.g. have something like <div id='foo' @onchange=HandleHello></div> in your Blazor code, then in your JS code dispatch a change event to foo whenever you capture a hello event. Commented May 18, 2021 at 23:45
  • Thanks for the tip. i can still change to version 6 prev 2 i just started building the app. What is your sugestion? Commented May 18, 2021 at 23:54
  • 2
    Go to .NET 6 preview 3, then you can do this: devblogs.microsoft.com/aspnet/… Commented May 19, 2021 at 1:07

2 Answers 2

15

For anyone wondering the solution proposed by @Mister Magoo is no longer a preview for .NET 6 and is documented here with some exemples.

In a nutshell :

Create a C# class with the EventHandlerAttribute :

[EventHandler("oncityclicked", typeof(CustomSelectionCityEventArgs),
    enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}

public class CustomSelectionCityEventArgs: EventArgs
{
    public Guid Id { get; set; }
}

Add JS inside ./wwwroot/index.html and after <script src="_framework/blazor.webview.js" autostart="false"></script> :

<script>
    Blazor.registerCustomEventType('cityclicked', {
        createEventArgs: event => {
            return {
                id: event.detail
            };
        }
    });
</script>

In you razor :

@code {
    private void HandleCityClicked(CustomSelectionCityEventArgs eventArgs)
    {
        Console.WriteLine("Bouep");
    }
}

<div id="CarteLeaflet" @oncityclicked="HandleCityClicked"></div>

And finally in the JS you can dispatch the event :

function OnMarkerClick(pId) {
    const event = new CustomEvent('cityclicked', {
        bubbles: true,
        detail: pId
    });
    document.getElementById('CarteLeaflet').dispatchEvent(event);
}

Don't make the same mistake as me, the event name in the C# should start with "on" (JS : "cityclicked", C# : "oncityclicked").

Sign up to request clarification or add additional context in comments.

Comments

11

For custom events, you will need to manually utilize JavaScript/.NET interoperability.

Using the Instance Method Call method:

  • Pass the .NET instance by reference to JavaScript:
    • Make a static call to DotNetObjectReference.Create.
    • Wrap the instance in a DotNetObjectReference instance and call Create on the DotNetObjectReference instance. Dispose of DotNetObjectReference objects (an example appears later in this section).
  • Invoke .NET instance methods on the instance using the invokeMethod or invokeMethodAsync functions. The .NET instance can also be passed as an argument when invoking other .NET methods from JavaScript.

Example

Note, this is a very simplified example. You probably want to add a few things; start by IDisposable on your interop classes to avoid memory leaks.

  1. In C#, create a helper class to manage the interop:
public class CustomEventHelper
{
    private readonly Func<EventArgs, Task> _callback;

    public CustomEventHelper(Func<EventArgs, Task> callback)
    {
        _callback = callback;
    }

    [JSInvokable]
    public Task OnCustomEvent(EventArgs args) => _callback(args);
}

public class CustomEventInterop : IDisposable
{
    private readonly IJSRuntime _jsRuntime;
    private DotNetObjectReference<CustomEventHelper> Reference;

    public CustomEventInterop(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public ValueTask<string> SetupCustomEventCallback(Func<EventArgs, Task> callback)
    {
        Reference = DotNetObjectReference.Create(new ScrollEventHelper(callback));
        // addCustomEventListener will be a js function we create later
        return _jsRuntime.InvokeAsync<string>("addCustomEventListener", Reference);
    }

    public void Dispose()
    {
        Reference?.Dispose();
    }
}
  1. In a Blazor component, add an instance of the interop class (Interop) and add a local method as a callback (HandleCustomEvent):
private CustomEventInterop Interop { get; set; }

protected override async Task OnAfterRenderAsync(bool firstRender) {
    if (!firstRender)
    {
        return;
    }
    Interop = new(JS);
    await Interop.SetupCustomEventCallback(args => HandleCustomEvent(args));
    HasRendered = true;
}

private void HandleCustomEvent(EventArgs args) {
    // ... handle custom event here
}
  1. In JavaScript, add a method that references the DotNetObjectReference and can call the interop in C#:
function addCustomEventListener(dotNetObjectRef) {
  document.addEventListener('hello', (event) => {
    // Calls a method by name with the [JSInokable] attribute (above)
    dotNetObjectRef.invokeMethodAsync('OnCustomEvent')
  });
}

If using TypeScript, you might check out this GitHub Issue.

12 Comments

I Only want to a subscibe to a event created in javascript. There are no button or ui elements used. IT's like the observer patern
@enet unless you are using .NET 6 preview, JS interop is the best way to handle real custom events. mrmagoo's hack may work, but it's still that: a hack.
Let's not be confused, moving to .NET6 P2/3 and using a custom event type is the "blazor way" but is still in preview. Anything else is a workaround. I wouldn't personally use this method but it will certainly work and is a solid way to do it.
Exactly, and this isn't the only way to use JS interop to achieve an event listener in Blazor. Until .NET 6 ships, I think JS interop is the most robust way to solve the problem from the question. It could even be expanded to accept a serializable payload from js, which you can't do with the @onclick workaround. If you don't need anything fancy, then @mister magoo has given you a fine workaround as well. I suspect more appealing answers will arrive when .NET 6 ships.
Even now, after .Net6 shipped, I cant make the .net6 way work.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.