JavaScript Interop in Blazor - Executing C# From JavaScript

One of the main features offered by Blazor is the ability to write C# code that executes in the browser. While this reduces the need for JavaScript in your UI layer, it does not necessarily negate it entirely. There are limitations to the things that WebAssembly modules can do. For example, WebAssembly modules do not have access to the browser's DOM, nor any Web APIs such as local storage, FileReader, Geolocation etc. If you want to make use of these APIs, or indeed if you want to make use of existing JavaScript libraries such as a proven, full featured rich text editor, you need to create a layer between Blazor and JavaScript.

Within Blazor development, the foundation for this layer is known as JavaScript Interop. It is two-way, in that it enables communication from C# code to JavaScript, and back again from JavaScript into C# code.

On this page, you can learn about communicating with C# from JavaScript. For guidance on communicating with JavaScript From C#, see JavaScript Interop in Blazor - Executing JavaScript From C#.

Calling C# Methods From JavaScript

Any C# method that you want to invoke from JavaScript must be decorated with the [JSInvokable] attribute and any arguments that the method accepts must be serialisable to JSON. The method name must be unique to the assembly that it belongs to, and it must be public. Once that is done, the approach you take will depend on whether the C# method is a an instance method or a static method.

The official recommendation is to use async APIs for JavaScript interop. This is because async is required for interop in Blazor Server applications. Consequently, your code will continue to work if you converted a WebAssembly application to a server application.

Calling Static Methods

The Razor component below includes a public static method that takes a string and reverses its characters, returning the result wrapped in a Task:

@page "/interop"
  
<h3>Interop Demo</h3>
  
@code {
    [JSInvokable]
    public static Task InteropReverse(string input)
    {
        var result = new String(input.Reverse().ToArray());
        return Task.FromResult(result);
    }
}

The JSInvokable attribute identifies this method as allowing invocation from JavaScript code.

The next step is to create a JavaScript function that calls the C# method:

window.reverseString = (input) => {
    DotNet.invokeMethodAsync("LearnBlazor.Client", "InteropReverse", input)
        .then(result => {
            alert(result);
        });
}

The JavaScript reverseString method uses the DotNet.invokeMethodAsync method provided by Blazor interop. It takes the name of the assembly where the C# method resides, the name of the C# method to be invoked, and any arguments that the C# method requires. In this example, the C# method is in the Client project of an application built using the ASP.NET Hosted template.

The asynchronous DotNet.invokeMethodAsync method returns a Promise which is resolved, and the return value is displayed in a browser alert.

For the purposes of demonstration, this method is placed in a file named interop.js and is referenced in index.html:

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
    <script src="js/interop.js"></script>
</body>

Finally, the JavaScript reverseString method is invoked from within the Razor Component using interop to call a JS method from C# code, with a value passed to its input parameter:

@page "/interop"
@inject IJSRuntime JSRuntime

<h3>Interop Demo</h3>

<button @onclick=CallReverse>Click</button>
  
@code {
    async Task CallReverse()
    {
        await JSRuntime.InvokeVoidAsync("reverseString", "!ekiM olleH");
    }
  
    [JSInvokable]
    public static Task InteropReverse(string input)
    {
        var result = new String(input.Reverse().ToArray());
        return Task.FromResult(result);
    }
}

When you click the button, the component's CallReverse method is executed, which calls the JavaScript reverseString method, which in turn calls the C# InteropReverse method, passing in "!ekiM olleH" as an argument. Once the C# method has executed, the reversed result is passed back to JavaScript, which displays the value in an alert:

JavaScript interop

Calling Instance Methods

In C#, invoking an instance method requires that you instantiate an object and call the method on that object. Blazor's interop framework enables you to pass a reference to the instantiated DotNet object to the JavaScript method that invokes a method on it.

In this example, the method that will be called from JavaScript is the Reverse method in the following StringHelpers class:

public class StringHelpers
{
    private readonly string _input;
    public StringHelpers(string input) => _input = input;
    [JSInvokable("LearnBlazor.Client")]
    public string Reverse() => new String(_input.Reverse().ToArray());
}

The method has been decorated with the JSInvokable attribute, but this time, the overload that takes an identifier is used so that the name of the assembly can be provided.

The alterations to the Razor component are highlighted:

@page "/interop"
@inject IJSRuntime jsRuntime
@implements IDisposable
  
<h3>Interop Demo</h3>
  
<button @onclick=CallReverse>Click</button>
  
@code {
    string input { get; set; } = "!ekiM olleH";
    DotNetObjectReference<StringHelpers> stringHelpersRef;  
  
    async Task CallReverse()
    {
        stringHelpersRef = DotNetObjectReference.Create(new StringHelpers(input));
        await jsRuntime.InvokeVoidAsync("reverseString", stringHelpersRef);
    }  
  
    public void Dispose()
    {
        stringHelpersRef?.Dispose();
    }
}

The component now implements IDisposable. A DotNetObjectReference is declared, which is intended to represent the reference to the StringHelpers instance. This is created in the CallReverse method via the DotNetObjectReference static Create method. It is then passed as a parameter to the InvokeVoidAsync method. Finally, the object reference is disposed to prevent memory leaks.

The interop.js file contents are amended as follows:

window.reverseString = (stringHelpersRef) => {
    stringHelpersRef.invokeMethodAsync("LearnBlazor.Client", "Reverse")
        .then(result => {
            alert(result);
        });
    stringHelpersRef.dispose();
}

The object reference that was passed as a parameter is used as a proxy to invoke the Reverse method in the StringHelpers class. The result is displayed in an alert as before, and then the dispose method is called on the object reference.

Last updated: 15/02/2023 09:02:24

Latest Updates

© 2023 - 2024 - Mike Brind.
All rights reserved.
Contact me at Outlook.com