In this article, we’ll look at Blazor, a single-page web app framework built on .NET that runs in the browser with WebAssembly. We’ll get an understanding of how Blazor handles JavaScript calls, why it’s necessary, and how it’s used.
As WebAssembly ( abbreviated Wasm) is gaining momentum it’s important to understand the current capabilities. WebAssembly lacks the ability to directly access the Browser’s DOM API, however, it can make calls to JavaScript. Because of this shortcoming JavaScript is still very much a part of web development.
WebAssembly (Wasm) is a binary instruction format that is designed to provide a compilation target for high-level languages like C#. Recently Microsoft began experimenting with WebAssembly to bring .NET to the browser using the Mono run-time. Mono provides the basic plumbing allowing .NET libraries (.dll’s) to run on WebAssembly.
Blazor features a component architecture, routing, a virtual DOM, and a JavaScript Interoperability (interop) API. Through the JavaScript interop a Blazor app can invoke JavaScript functions from .NET and C# methods from JavaScript code.
To call into JavaScript from .NET the IJSRuntime abstraction is used. The current instance of IJSRuntime is resolved by making a call to JSRuntime.Current. From this instance we can call the InvokeAsync<T> method passing in the first argument as an identifier to the corresponding JavaScript function we would like to invoke, this function must be available on the global scope of window. Additional arguments may be passed through to the JavaScript function provided they are JSON serialize-able as well as the return type Task<T>.
Since Blazor is built upon Mono and WebAssembly and therefore has no direct access to the browser’s DOM API, it must marshal calls through JavaScript when it needs DOM access. The inclusion of JavaScript in the stack is not only beneficial in terms of necessity, but also flexibility.
Backwards compatibility
Including JavaScript in the stack enables Blazor applications to utilize existing JavaScript libraries. This includes UI libraries like Bootstrap, Toastr.js, a toast notification library, and Chart.js for simple charting components.
In addition, full-featured commercial UI libraries such as Kendo UI could potentially be ported to Blazor. These “ports” essentially provide a C# API surface for interacting with the underlying JavaScript while providing a migration path for users.
Mind the gap
Because Blazor is new and experimental, the interop allows developers to fall back on JavaScript when there are shortcomings of WebAssembly itself, or because the Blazor framework is not yet mature.
For example, if we wanted to use a standard window prompt() method, there is no native support in Blazor to do this. However, a simple API can be created using the JavaScript interop to add support for this functionality.
We’ll start by creating a JavaScript file with the method we would like to invoke from our application.
For the function to be visible to Blazor, we’ll need to add it to the scope of window. As a best practice, additional namespaces can be added using a module pattern, this protects our methods from conflicting with other code on the scope of window. Within our namespace, we define a function to call the native window prompt() method.
Next, we need to invoke the JavaScript showPrompt function from within C# using the JSRuntime.Current.InvokeAsync method. A C# function PromptAsync provides a nice abstraction that can be used within the Blazor application. Developers using the abstraction will not need to understand the underlying JavaScript implementation.
Since Blazor’s UI process is capable of running on a separate thread from the application InvokeAsync should be used by default.
However, if there is a need to invoke the JavaScript method synchronously, we can provide that functionality by downcasting JSRuntime to IJSInProcessRuntime. Adding the Prompt method in addition to PromptAsync provides an alternative API when asynchronous behavior is not available.
The ShowPrompt method is now available to use within the application. We can call PromptAsync from a Blazor component by calling the method and awaiting a result.
In the following example, we’ll trigger a browser prompt when the user clicks on the component. When the prompt is closed the result is returned to the component’s Message field which is data-bound and rendered to the component. To ensure the new value is updated when data-binding occurs, we’ll call StateHasChanged to instruct Blazor to re-render the component.
While Blazor and WebAssembly lack the ability to directly access the Browser’s DOM API, the JavaScript interop provides a means of filling the gap. The interop makes it possible to migrate existing JavaScript libraries to Blazor. Through the interop developers can create abstractions around browser features providing C# methods to add functionality at the application level.
As Blazor gains in popularity, it is reasonable to assume that an ecosystem of interop libraries will emerge. As more interop libraries become available, then Blazor developers may spend less time writing JavaScript and more time in C#.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowToast notifications are messages that appear on the screen to provide feedback to users. When users interact with the user […]
Deno’s features and built-in TypeScript support make it appealing for developers seeking a secure and streamlined development experience.
It can be difficult to choose between types and interfaces in TypeScript, but in this post, you’ll learn which to use in specific use cases.
This tutorial demonstrates how to build, integrate, and customize a bottom navigation bar in a Flutter app.