Understanding JavaScript Interop In Blazor

Blazor is a .NET framework that runs in the browser on a real.NET runtime (Mono) via Web Assembly. Blazor is an experimental project and right now, it is not in production and so many changes are going on. Web Assembly is a web standard which defines a binary format just like a lower level assembly language. So, it enables executing code nearly as fast as running native machine code.
 
It was developed at the W3C (World Wide Web Consortium). It defines an AST (Abstract Syntax Tree) which gets stored in a binary format. It is supported by all the major browsers without any plugins. Mono is a Microsoft .NET Framework implementation based on the ECMA standards for C# and CLR (Common Language Runtime). Mono's runtime is compiled into Web Assembly and its IL interpreter is used to run managed code.
 
In most conditions, when we do programming with Blazor, C# code will execute in the browser via Web Assembly. Blazor application can also invoke the JavaScript function from C# (.NET) and vice versa. This feature is referred to as "JavaScript Interop".
 
Calling JavaScript function from C# (.net) code
 
There are many scenarios when we need to call JavaScript function from Blazor application. Blazor applications use the IJSRuntime interface to call JavaScript from the .NET application. This interface can be accessed using the JSRuntime.Current static property. Using InvokeAsync<T> method of IJSRuntime abstraction, we can call the JavaScript function. This method takes function name and function parameters as the argument.
  1. Task<T> InvokeAsync<T>(string identifier, params object[] args);  
Example
 
In the following example, I have invoked JavaScript function on the page init method.
  1. protected override async Task OnInitAsync()  
  2. {  
  3.     await JSRuntime.Current.InvokeAsync<object>("CalledJSFunction");  
  4. }  
Here, "CalledJSFunction" is the JavaScript function name that is called on page initialization. We can define JavaScript function either in index.html (under wwwroot folder) or in a separate JavaScript file that needs to be registered after registering blazor.webassembly.js.
 
Index.html 
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     ....  
  5.     ....  
  6. </head>  
  7. <body>  
  8.     <app>Loading...</app>  
  9.   
  10.     <script src="_framework/blazor.webassembly.js"></script>  
  11.     <script>  
  12.         function CalledJSFunction() {  
  13.             alert("Hello Reader!");  
  14.         }  
  15.     </script>  
  16. </body>  
  17. </html>  
Output

JavaScript Interop in Blazor 
 
We can also pass the parameters to the JavaScript function using the second argument of the InvokeAsync method. This will accept value as objects. In the following Example, I have passed my name as a JavaScript function argument. 
  1. protected override async Task OnInitAsync()  
  2. {  
  3.    await JSRuntime.Current.InvokeAsync<object>("CalledJSFunctionWithParameter""Jignesh Trivedi");  
  4. }  
Index.html
  1. function CalledJSFunctionWithParameter(name) {  
  2.    alert("Hi, This is " + name);  
  3. }  
JavaScript Interop in Blazor 
 
Calling .net (C#) function from JavaScript
 
Blazor application enables us to call .NET (C#) method from JavaScript. There are two JavaScript:DotNet.invokeMethod and DotNet.invokeMethodAsunc functions defined that are used to call the C# method from JavaScript. Here, we also need to pass the assembly containing this function. Following is the definition of the invokeMethodAsync method.
  1. DotNet.invokeMethodAsync(assemblyName, functionname).then(data => { ... })  
To invoke C# method from JavaScript, the method must have the following characteristics.
  • The method must be decorated with "JSInvokable" attribute
  • The method must be public
  • The method may either be static or instance level (this is only supported by Blazor 0.5.0 and above)
  • The Identifier for the method must be unique
  • The method must be non-generic
Example
 
The following function is called from the JavaScript. I have defined this function as static within DemoComponent and here, it is just returning a static string from this function.
  1. //DemoComponent.cs
  2. [JSInvokable("CalledByComponentScript")]  
  3. public static string CalledByComponentScript()  
  4. {  
  5.     return "Hello from C#";  
  6. }  
  1. //Index.html
  2. function CalledCSFunction() {  
  3.     DotNet.invokeMethodAsync("JavaScriptInterop"'CalledByComponentScript')  
  4.         .then(data => {  
  5.             alert(data)  
  6.         })  
  7. }  
I have called this function on button click from demo.cshtml file.
 
demo.cshtml
  1. <button class="btn btn-success" onclick="CalledCSFunction()">Call Java Script</button>  
Output
 
JavaScript Interop in Blazor 
 
We can also pass the parameters to the C# (.NET) function using third argument of DotNet.invokeMethodAsync method. This will accept the value as objects. In the following example, I have passed my name as JavaScript function argument.
  1. //DemoComponent.cs
  2. [JSInvokable("CalledByComponentScriptWithParameter")]  
  3. public static string CalledByComponentScript(string name)  
  4. {  
  5.     return "Hello, " + name + "!";  
  6. }  
  1. //Index.html
  2. function CalledCSFunctionWithParameter() {  
  3.     var yourName = "Jignesh Trivedi"  
  4.     DotNet.invokeMethodAsync("JavaScriptInterop"'CalledByComponentScriptWithParameter', yourName)  
  5.         .then(data => {  
  6.             alert(data)  
  7.         })  
  8. }  
I have called this function on button click from the demo.cshtml file.
 
demo.cshtml
  1. <button class="btn btn-success" onclick="CalledCSFunctionWithParameter()">Call Java Script (with parameter)</button>  
Output 
 
 JavaScript Interop in Blazor
 
In the preceding, I have called static C# methods from a JavaScript function. The Blazor 0.5.0 allows calling .NET instance method from the JavaScript. To do this, we need to pass the instance of the class to JavaScript by using DotNetObjectRef class instance. This class instance passes our object instance to the JavaScript and we can invoke the method using the passed object instance using the invokeMethod or invokeMethodAsync functions.
 
Example
 
In the following example, I have created a class named "MyHelper" and passed the instance of this class to the JavaScript in the Init method of the component. In JavaScript, I have held this instance and called the C# method on button click. In the instance method, I returned the string as a parameter.
  1. public class MyHelper  
  2. {  
  3.     [JSInvokable("InstanceMethod")]  
  4.     public string InstanceMethod(string myMessage)  
  5.     {  
  6.         return myMessage;  
  7.     }  
  8. }  
  1. //DemoComponent  
  2. protected override async Task OnInitAsync()  
  3. {  
  4.     await JSRuntime.Current.InvokeAsync<object>("passInstanceOfHelper"new DotNetObjectRef(new MyHelper()));  
  5. }  
  1. //Index.html  
  2. var myObject;  
  3. function passInstanceOfHelper(instance) {  
  4.     myObject = instance;  
  5. }  
  6.   
  7. function CallInstanceMethod() {  
  8.     myObject.invokeMethodAsync('InstanceMethod'"Hello Friends!").then(r => {  
  9.         alert(r);  
  10.     });  
  11. }  
Output
 
JavaScript Interop in Blazor 
 
If we call C# function from JavaScript and update any C# variable/property that's visible on screen, the change is not getting reflected. Blazor component use "StateHasChanged" method to update the UI component. But this function cannot be called in the static method and the instance method is in a different class. This can be achieved by using the instance method and passing an object of current component to that instance.
  1. //DemoComponent  
  2. public class DemoComponent : BlazorComponent  
  3. {  
  4.     public string message = "";  
  5.     protected override async Task OnInitAsync()  
  6.     {  
  7.          await JSRuntime.Current.InvokeAsync<object>("passInstanceOfHelperUIUpdate"new DotNetObjectRef(new MyHelper(this)));  
  8.     }  
  9.     public void UpdateUI()  
  10.     {  
  11.         this.StateHasChanged();  
  12.     }  
  13. }  
  1. public class MyHelper  
  2. {  
  3.     DemoComponent _component;  
  4.     public MyHelper(DemoComponent component)  
  5.     {  
  6.         _component = component;  
  7.     }  
  8.     [JSInvokable("UIUpdateExample")]  
  9.     public void UIUpdateExample()  
  10.     {  
  11.         _component.message = "Hello, Reader!";  
  12.         _component.UpdateUI();  
  13.     }  
  14. }  
  1. //Index.html  
  2. var myClassObject;  
  3. function passInstanceOfHelperUIUpdate(instance) {  
  4.     myClassObject = instance;  
  5. }  
  6.   
  7. function updateUIExample() {  
  8.     myClassObject.invokeMethodAsync('UIUpdateExample').then(r => {  
  9.         console.log('Hello!');  
  10.     });;  
  11. }  
  1. //Demo.cshtml  
  2. <div class="col-md-4">  
  3.     <button class="btn btn-success" onclick="updateUIExample()">Update UI Example</button>  
  4. </div>  
  5. <br />  
  6. @message  
Output
 
JavaScript Interop in Blazor 
 
Summary
 
Using JavaScript Interop functionality, we can call JavaScript function from C# (.net) and also we can call C# (.NET) method from JavaScript.
 
You can view or download the source code from the GitHub link here


Similar Articles