Search code examples
javascript.netblazorinteropwebassembly

Blazor listen to javascript event


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,


Solution

  • 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.