Blazor: JavaScript Interop Accessing JavaScript Functions in Blazor Components

Blazor, being the great technology for interactive Web UI, we can use it for modern front-end web applications. If you have an experience of other front-end technologies like React.js/Angular, you must have experience of writing modular code using JavaScript. We write the front-end application code in separate JavaScript files and using ES 6 modules (export / import) we access code of one JavaScript file other.

While using Blazor for front-end application, it may be an application’s requirement that the code written in JavaScript file need to be accessed in Blazor component. Yes, it is possible in Blazor component using IJSRuntime interface.


The IJSRutime Interface
To access JavaScript functions from Blazor application, the IJSRuntime interface must be injected in the Blazor Component. This interface has following methods

• InvokeAsync
 o The method to access JavaScript function that returns value
• InvokeAsync<Tvalue>
 o The TValue, represents the type of value returned from the JavaScript function.
• InvoneVoidAsync
 o The method to access JavaScript function that does not return value
Above methods have following syntax

IJSRuntime.InvokeAsync(“<identifier>”, Object[])


The first parameter is identifier as a string. This is name of the JavaScript function that will be called using .NET code. This identifier is relative to the windows object. For example, if the JavaScript function is window.namespace.myfunction, then the identifier name will be name will be namespace.myfunction. The Object [] array represents number of arguments to be passed to JavaScript function. These parameters will be serialized in the JSON form. The most important part of these methods is an asynchronous execution. The reason behind the asynchronous execution is that the JS PROMISE object. The Promise object is returned for all Invoke methods of IJSRuntime. These methods unwrap the Promise returns a value awaited by the promise. Blazor application access these functions asynchronously.

The Implementation
Important Note: The tutorial explains the Blazor-JavaScript Interop and it puts light on the concept, so the code of the tutorial is very simple.

In this tutorial we will invoke JavaScript function in Blazor Component. Visual Studio 2019 with latest update and .NET 5 SDK is used for implementing code for this tutorial. Alternatively, you can use Visual Studio on Mac and Visual Studio code on Mac as well as on Linux.

Step 1: Open Visual Studio and create a new Blazor Web Assembly. Name this project as Blazor_JavaScriptInteroperability.
Step 2: In the wwwroot folder of the project add a new JavaScript file and name this file as myscript.js. We will add 3 methods in this file as shown in Listing 1
function display() {
	alert('The Button is clicked');
}

function add(x, y) {
	var res = x + y;
	return res;
}

function getData() {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.onload = function () {
 
            if (xhr.status === 200) {
 
                console.log('Reponse received ' + xhr.response);
                resolve(xhr.response);
            }
        };
        xhr.onerror = function () {
            reject('Error Occurred ' + xhr.responseText);
        };
        xhr.open('GET', "https://myapiapp.azurewebsites.net/api/Products");
        xhr.send();
    });
}


Listing 1: The myscript.js

Code in listing contains three methods. The display () method is does not accept any arguments and does not return any value. The add() method accepts two arguments and return the result as addition of these arguments. The getData() method is using XmlHttpRequest object to perform AJAX calls. The getData() method returns JavaScript Promise object. (Please Note: For the simplicity I have used the XmlHttpRequest object, you can use fetch object for HTTP operations.)

Step 3: Modify index.html file from the wwwroot folder and refer the myscript.js file in the script reference as shown in Listing 2
  <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="./myscript.js"></script>
  </body>

  

Listing 2: Using the JavaScript file in index.html

Make sure that, the script reference is added in the body tag after the bazor.webassembly.js script reference. This is major step for implementing JavaScript interoperability in Blazor Applications. When the application is executed, Blazor Web Assembly Loads all resources in the browser and the rendering starts from index.html. All scripts referenced in the index.html are loaded in the browser. These scripts are available to .NET code using IJSRuntime interface and the Blazor component can access methods from the JavaScript files.

Step 4: In project add a new folder and name it as Models. In this folder add a new class file and name it as Product.cs. In this file add code for the Product class as shown in the Listing 3.

public class Product
{
	public int ProductRowId { get; set; }
	public string ProductId { get; set; }
	public string ProductName { get; set; }
	public string CategoryName { get; set; }
	public string Manufacturer { get; set; }
	public string Description { get; set; }
	public int BasePrice { get; set; }	
}	


Listing 3: The Product class

Accessing methods from the JavaScript file in .NET code

Step 4: In the project add a new folder and name it as Services. In this folder add a new class file and name this file as CallJavaScript.cs. In this file add the code as shown in the Listing 4

using Blazor_JavaScriptInteroperability.Models;
using Microsoft.JSInterop;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
namespace Blazor_JavaScriptInteroperability.Services
{
	public class CallJavaScript
	{
		private  readonly IJSRuntime js;
		public int res = 0;
		string data;
		public List<Product> products;

		public CallJavaScript(IJSRuntime js)
		{
			this.js = js;
			products = new List<Product>();
		}

		public  async void Print()
		{
			await js.InvokeVoidAsync("display");
		}

		public async Task Add()
		{
			res = await js.InvokeAsync<int>("add", 10, 20);
		}

		public async Task GetData()
		{
			data = await js.InvokeAsync<string>("getData");
			products = JsonSerializer.Deserialize<List<Product>>(data);
		}
	}
}


Listing 4: Access the JavaScript file in .NET Code

Code shown in the Listing 4 contains CallJavaScript class. This class is constructor injected using IJSRuntime interface. The CallJavaScript class contains following methods

 • Print()
  o This method invokes display () method from JavaScript file using InvokeVoidAsync () method of the IJSRuntime. This display () method does not accept any arguments and return any value.

 • Add()
  o This method invokes add () method from JavaScript file using InvokeAsync method. The type T parameter represents the return type from the add () method. The add () method accepts two arguments, these arguments are passed to add () method using params object[] which is the second parameter to InvokeAsync() method. The params object [] will be serialized as JSON.

 • GetData()
  o This method invokes getData () method from JavaScript file. The getData () method performs AJAX call and returns the Promise object. In the .NET code we are using async..await to make sure that the Promise object returns must be subscribed and once the promise object is resolved the response data is received and processed further. The returned data after resolving the promise is deserialized into the product List type.

Code in the Listing 4 demonstrate use of all the 3 methods of the IJSRuntime interface.

Using the CallJavaScript class into the component

Step 5: In the Pages folder add a new Blazor component. Name this component CallJsInterOP.razor. In this component add the code as shown in the Listing 5

@page "/calljsusingclass"
@using Blazor_JavaScriptInteroperability.Services
@inject IJSRuntime jsInterop
<h3>Calling JS in C# using C# Class</h3>

<table class="table table-bordered table-striped">
	<tr>
		<td>
			<input type="button" value="Print" class="btn btn-warning"
				   @onclick="obj.Print" />
		</td>
		<td>
			<input type="button" value="Add" class="btn btn-dark"
				   @onclick="obj.Add" />
		</td>
		<td>
			<input type="button" value="Get Data" class="btn btn-dark"
				   @onclick="obj.GetData" />
		</td>
	</tr>
</table>
<hr />
<div>
	<strong>
		The Result of 'add()' method is @obj.res
	</strong>
</div>
<hr />
<div>
	 
	<table class="table table-bordered table-striped">
		<thead>
			<tr>
				<th>Product Row Id</th>
				<th>Product Id</th>
				<th>Product Name</th>
				<th>Category Name</th>
				<th>Manufacturer</th>
				<th>Description</th>
				<th>Base Price</th>
			</tr>
		</thead>
		<tbody>
			@foreach (var product in obj.products)
			{
			<tr>
				<td>@product.ProductRowId</td>
				<td>@product.ProductId</td>
				<td>@product.ProductName</td>
				<td>@product.CategoryName</td>
				<td>@product.Manufacturer</td>
				<td>@product.Description</td>
				<td>@product.BasePrice</td>
			</tr>
			}
		</tbody>
	</table>
</div>
@code {
	private CallJavaScript obj;

	protected override void OnInitialized()
	{
		obj = new CallJavaScript(jsInterop);
		base.OnInitialized();
	}
}



Listing 5: The component

The component code of Listing 5 uses the @inject directive to inject the IJSRuntime in the component. The IJSRuntime object is passed to the constructor of the CallJavaScript class using the OnInitialized() method of the component. The component contains 3 HTML buttons. These buttons are bound with methods of the CallJavaScript class.

Step 6: Modify the NavMenu.razor file in the Shared folder to add the Navigation Link for the newly added component as shown in Listing 6

<li class="nav-item px-3">
			<NavLink class="nav-link" href="calljsusingclass">
				<span class="oi oi-list-rich" aria-hidden="true"></span> Call JS in .NET Class
			</NavLink>
		</li>



Listing 6: Adding the Navigation Link for the newly added component

The application execution verification

Run the application (you can use any chromium-based browser on Windows/Linux/Mac). The application will be loaded in the browser as shown in Figure 1



Figure 1: The application loaded in the browser

You can see the myscript.js file downloaded in the browse from the Network tab of the developer tools as shown in Figure 2



Figure 2: The JS file downloaded in the browser

Click on the Call JS in .NET Class link, the CallJsInterOP component will be loaded as shown in Figure 3



Figure 3: The CallJsInterOP component

Click on the Print button, the alert box will be displayed as shown in Figure 4.



Figure 4: The Alert box displayed after clicking on Print button

Click on the Add button, the addition of numbers those are hard coded in the Listing 4 will be displayed as shown in Figure 5



Figure 5: The result of addition of two numbers result returned from JavaScript code

Click on the Get Data button, the call to JavaScript method will be executed and the Products List will be returned. This list will be displayed on component as shown in Figure 6



Figure 6: The Product List received from the AJAX class made by the JavaScript code

The Figure 6 shows the Products List. Here we can experience an easy coding using .NET and its IJSRuntime interface that easily unwraps the JavaScript Promise object and receive the resolved data easily.
The code for this article can be downloaded from this link

That’s it

Conclusion: When developing Front-End apps using Blazor, if you want to use existing logic written JavaScript files, then it becomes easy to use this logic in Blazor applications using IJSRuntime. This feature makes it easy to migrate your existing JavaScript applications to Blazor applications.

Popular posts from this blog

Uploading Excel File to ASP.NET Core 6 application to save data from Excel to SQL Server Database

ASP.NET Core 6: Downloading Files from the Server

ASP.NET Core 6: Using Entity Framework Core with Oracle Database with Code-First Approach