Day 8: JavaScript Interop
Call JavaScript from C# and vice versa, use JS isolation, and integrate JS libraries.
1. Why JavaScript Interop?
Access browser APIs (e.g.,
localStorage, geolocation).Use existing JavaScript libraries (e.g., charts, animations).
Direct DOM manipulation when needed.
2. Call JavaScript from C# (IJSRuntime)
Use IJSRuntime to invoke JS functions.
Example: Show a Browser Alert
@page "/js-demo"
@inject IJSRuntime JSRuntime
<button @onclick="ShowAlert">Show Alert</button>
@code {
private async Task ShowAlert()
{
await JSRuntime.InvokeVoidAsync("alert", "Hello from Blazor!");
}
}
3. Call C# from JavaScript
Use DotNetObjectReference to pass C# methods to JS.
Example: JS Timer that Updates Blazor UI
Component (C#):
@page "/timer"
@implements IDisposable
@inject IJSRuntime JSRuntime
<h3>Timer: @currentCount</h3>
@code {
private int currentCount = 0;
private DotNetObjectReference<TimerComponent>? dotNetRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetRef = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("startTimer", dotNetRef);
}
}
[JSInvokable]
public void UpdateTimer() => currentCount++;
public void Dispose() => dotNetRef?.Dispose();
}
JavaScript (wwwroot/js/timer.js):
function startTimer(dotNetRef) {
setInterval(() => {
dotNetRef.invokeMethodAsync('UpdateTimer');
}, 1000);
}
Import JS in index.html (Blazor WebAssembly):
<script src="js/timer.js"></script>
Run HTML
4. JavaScript Isolation (ES6 Modules)
Load JS modules directly in components for better organization.
Component:
@page "/module-demo"
@inject IJSRuntime JSRuntime
<button @onclick="CallModule">Call JS Module</button>
@code {
private async Task CallModule()
{
var module = await JSRuntime.InvokeAsync<IJSObjectReference>(
"import",
"./js/utilities.js"
);
await module.InvokeVoidAsync("showConfetti");
}
}
JavaScript (wwwroot/js/utilities.js):
export function showConfetti() {
// Add confetti animation logic
}
5. Real-World Example: Clipboard API
Integrate the browser’s clipboard API to copy text.
Component:
@page "/clipboard"
@inject IJSRuntime JSRuntime
<input @bind="textToCopy" />
<button @onclick="CopyToClipboard">Copy</button>
@code {
private string textToCopy = "";
private async Task CopyToClipboard()
{
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", textToCopy);
}
}
6. Error Handling
Wrap JS interop calls in try-catch blocks:
try {
await JSRuntime.InvokeVoidAsync("someJSFunction");
}
catch (JSException ex) {
Console.WriteLine($"JS Error: {ex.Message}");
}
7. Practice Task
Create a WindowSize Component:
Use JS interop to get the browser window’s width/height.
Display the values in Blazor.
Update the values when the window resizes.
Solution:
@page "/window-size"
@inject IJSRuntime JSRuntime
@implements IDisposable
<h3>Window Size</h3>
<p>Width: @width px</p>
<p>Height: @height px</p>
@code {
private int width;
private int height;
private DotNetObjectReference<WindowSize>? dotNetRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
dotNetRef = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("setupResizeListener", dotNetRef);
}
}
[JSInvokable]
public void UpdateSize(int newWidth, int newHeight)
{
width = newWidth;
height = newHeight;
StateHasChanged();
}
public void Dispose() => dotNetRef?.Dispose();
}
JavaScript (wwwroot/js/windowResize.js):
function setupResizeListener(dotNetRef) {
window.addEventListener('resize', () => {
dotNetRef.invokeMethodAsync('UpdateSize', window.innerWidth, window.innerHeight);
});
}
8. Key Takeaways
IJSRuntime: Invoke JS functions from C#.DotNetObjectReference: Pass C# methods to JS.JS Isolation: Use ES6 modules for cleaner code.
Error Handling: Catch JS exceptions in C#.