JavaScript Interop in ASP.NET Core Blazor
Blazor enables seamless communication between your .NET C# code and JavaScript. This is crucial for leveraging existing JavaScript libraries, accessing browser APIs not directly exposed to .NET, or performing DOM manipulations when necessary.
Calling JavaScript Functions from .NET
To call a JavaScript function from your Blazor component, you use the IJSRuntime
service. This service is injected into your component, and its InvokeAsync
method is used to execute JavaScript code.
Basic Invocation
You can invoke a JavaScript function by providing its name and any arguments.
Example in a Blazor component (.razor file):
@inject IJSRuntime JSRuntime
<button @onclick="CallJavaScriptAlert">Show Alert</button>
@code {
private async Task CallJavaScriptAlert()
{
await JSRuntime.InvokeVoidAsync("alert", "Hello from Blazor!");
}
}
Corresponding JavaScript (e.g., in wwwroot/js/site.js
):
function alert(message) {
window.alert(message);
}
Invoking JavaScript with Return Values
If your JavaScript function returns a value, you can use InvokeAsync
with a generic type parameter to capture the result.
Example in a Blazor component:
@inject IJSRuntime JSRuntime
<button @onclick="GetBrowserInfo">Get Browser Info</button>
<p>Browser Name: @browserName</p>
@code {
private string browserName = "N/A";
private async Task GetBrowserInfo()
{
browserName = await JSRuntime.InvokeAsync<string>("getBrowserName");
}
}
Corresponding JavaScript:
function getBrowserName() {
return navigator.appName;
}
Invoking JavaScript from Modules
For better organization, you can load JavaScript as modules. Use JSRuntime.InvokeAsync
with the '@'
prefix to refer to module imports.
Example in a Blazor component:
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
<button @onclick="LogToConsole">Log to Console</button>
@code {
private async Task LogToConsole()
{
await JSRuntime.InvokeVoidAsync("eval", @"
import('/js/module.js').then(module => {
module.logMessage('Logged from Blazor via module!');
});
");
}
}
Corresponding JavaScript (e.g., in wwwroot/js/module.js
):
export function logMessage(message) {
console.log(message);
}
index.html
or _Host.cshtml
.
Calling .NET Methods from JavaScript
Blazor also allows you to call .NET methods from JavaScript. This is achieved using callbacks registered with the .NET runtime.
Registering Callbacks
You can use DotNet.invokeMethodAsync
from JavaScript to call a static or instance method on a .NET object.
Example in a Blazor component:
@inject IJSRuntime JSRuntime
@implements IDisposable
<button @onclick="RegisterCallback">Register Callback</button>
<p>Callback Result: @callbackResult</p>
@code {
private string callbackResult = "Waiting for JS...";
private DotNetObjectReference<MyComponent> dotnetRef;
protected override void OnInitialized()
{
dotnetRef = DotNetObjectReference.Create(this);
}
private async Task RegisterCallback()
{
await JSRuntime.InvokeVoidAsync("registerDotNetCallback", dotnetRef);
}
[JSInvokable]
public void ReceiveMessageFromJs(string message)
{
callbackResult = message;
StateHasChanged(); // Ensure UI updates
}
public void Dispose()
{
dotnetRef?.Dispose();
}
}
Corresponding JavaScript:
window.registerDotNetCallback = function(dotNetObjectReference) {
// Simulate a delay before calling back
setTimeout(() => {
dotNetObjectReference.invokeMethodAsync('ReceiveMessageFromJs', 'Hello from JavaScript!');
}, 1000);
};
Using DotNetObjectReference
The DotNetObjectReference
is essential for passing an instance of a Blazor component to JavaScript so that JavaScript can invoke methods on that specific instance.
DotNetObjectReference
instances to prevent memory leaks.
Working with the DOM
While Blazor's component model handles most UI updates, there might be scenarios where direct DOM manipulation is needed. JavaScript interop is the way to achieve this.
Example: Focusing an input element:
@inject IJSRuntime JSRuntime
@ref FocusInput
<input type="text" id="myInput" />
<button @onclick="FocusElement">Focus Input</button>
@code {
private ElementReference FocusInput;
private async Task FocusElement()
{
await JSRuntime.InvokeVoidAsync("focusElement", FocusInput);
}
}
Corresponding JavaScript:
function focusElement(element) {
element.focus();
}
Best Practices
- Minimize Interop Calls: Excessive interop can impact performance. Group multiple operations into a single call if possible.
- Use Modules: Organize your JavaScript code into modules for better maintainability and reusability.
- Handle Errors: Implement proper error handling in both your C# and JavaScript code for robust applications.
- Type Safety: When returning values from JavaScript, use specific types to ensure type safety in your C# code.
- Dispose References: Always dispose of
DotNetObjectReference
instances when they are no longer needed.