3.7 KiB
While a Blazor Server app is prerendering, certain actions, such as calling into JavaScript, aren't possible because a connection with the browser hasn't been established. Components may need to render differently when prerendered.
To delay JavaScript interop calls until after the connection with the browser is established, you can use the OnAfterRenderAsync component lifecycle event. This event is only called after the app is fully rendered and the client connection is established.
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<div @ref="divElement">Text during render</div>
@code {
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync(
"setElementText", divElement, "Text after render");
}
}
}
For the preceding example code, provide a setElementText
JavaScript function inside the <head>
element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). The function is called with IJSRuntime.InvokeVoidAsync
and doesn't return a value:
<script>
window.setElementText = (element, text) => element.innerText = text;
</script>
[!WARNING] The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.
The following component demonstrates how to use JavaScript interop as part of a component's initialization logic in a way that's compatible with prerendering. The component shows that it's possible to trigger a rendering update from inside OnAfterRenderAsync
. The developer must avoid creating an infinite loop in this scenario.
Where JSRuntime.InvokeAsync
is called, ElementRef
is only used in OnAfterRenderAsync
and not in any earlier lifecycle method because there's no JavaScript element until after the component is rendered.
StateHasChanged is called to rerender the component with the new state obtained from the JavaScript interop call. The code doesn't create an infinite loop because StateHasChanged
is only called when infoFromJs
is null
.
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JSRuntime
<p>
Get value via JS interop call:
<strong id="val-get-by-interop">@(infoFromJs ?? "No value yet")</strong>
</p>
Set value via JS interop call:
<div id="val-set-by-interop" @ref="divElement"></div>
@code {
private string infoFromJs;
private ElementReference divElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && infoFromJs == null)
{
infoFromJs = await JSRuntime.InvokeAsync<string>(
"setElementText", divElement, "Hello from interop call!");
StateHasChanged();
}
}
}
For the preceding example code, provide a setElementText
JavaScript function inside the <head>
element of wwwroot/index.html (Blazor WebAssembly) or Pages/_Host.cshtml (Blazor Server). The function is called with IJSRuntime.InvokeAsync
and returns a value:
<script>
window.setElementText = (element, text) => {
element.innerText = text;
return text;
};
</script>
[!WARNING] The preceding example modifies the Document Object Model (DOM) directly for demonstration purposes only. Directly modifying the DOM with JavaScript isn't recommended in most scenarios because JavaScript can interfere with Blazor's change tracking.