It is recommended to load JS libraries in the _Host.cshtml and not in the layout (nor in component where is not allowed)
But doing so, how is it possible to load some script for one page and some for another ? eg. I want the googlemaps stuff or the datatable stuff only in the pages where I use them, not in all (waste of space/memory/load times).
Thanks
Integrating JavaScript and Blazor is a little awkward because you can't add JavaScript to a Blazor C# file. However, you can add JavaScript to the Index. html page under the wwwroot folder in the project. That page (or any HTML page that works with Blazor), now just needs a script tag that references _framework/blazor.
Inject a script after Blazor starts cshtml (Blazor Server) when the app is initialized: Add autostart="false" to the <script> tag that loads the Blazor script. Inject a script into the <head> element markup that references a custom JS file after starting Blazor by calling Blazor.
Blazor supports existing JavaScript libraries using IJSRuntime, through JavaScript interop API's. Refer to the documentation Blazor JavaScript interop for more information.
There's one more way to do that using exported functions in JavaScript
wwwroot/js/script.js
export async function someFunction(parameter1, paramater2) {
...
}
Pages/CallJavaScript.razor
@page "/call-js"
@inject IJSRuntime JS
@code{
private IJSObjectReference myScript;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
myScript = await JS.InvokeAsync<IJSObjectReference>("import", "/js/script.js");
}
}
private async Task CallJavaScriptMethod()
{
myScript.InvokeVoidAsync("someFunction", "param1", "param2");
}
}
As discussed in the comments, Blazor is an SPA so any loaded script is available on Blazor pages since it's the same page.
However, you don't have to list them all in _Host.cshtml
, and indeed you probably want to only load a specific script when it's needed (e.g. not all users use a particular page/component where the script is required).
It is possible to load scripts dynamically using JS Interop. I created the following scriptLoader.js
library and included this in _Host.cshtml
:
// loadScript: returns a promise that completes when the script loads
window.loadScript = function (scriptPath) {
// check list - if already loaded we can ignore
if (loaded[scriptPath]) {
console.log(scriptPath + " already loaded");
// return 'empty' promise
return new this.Promise(function (resolve, reject) {
resolve();
});
}
return new Promise(function (resolve, reject) {
// create JS library script element
var script = document.createElement("script");
script.src = scriptPath;
script.type = "text/javascript";
console.log(scriptPath + " created");
// flag as loading/loaded
loaded[scriptPath] = true;
// if the script returns okay, return resolve
script.onload = function () {
console.log(scriptPath + " loaded ok");
resolve(scriptPath);
};
// if it fails, return reject
script.onerror = function () {
console.log(scriptPath + " load failed");
reject(scriptPath);
}
// scripts will load at end of body
document["body"].appendChild(script);
});
}
// store list of what scripts we've loaded
loaded = [];
This creates a script
element and appends to the body
element of the document. It returns a promise since the script will load asynchronously, so you need await
in the C# code.
The loaded
array is there to avoid re-loading the script again. Any script, once loaded, stays loaded unless the user refreshes the page. So the load only occurs once.
On a page/component where I need to ensure a library is loaded, I will need to inject the IJSruntime
...
@inject IJSRuntime jsRuntime
And then call it..
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// invoke script loader
Console.WriteLine("Loading jQuery");
await jsRuntime.InvokeVoidAsync("loadScript", "https://code.jquery.com/jquery-3.4.1.js");
await jsRuntime.InvokeVoidAsync("loadScript", "myJQueryTest.js");
Console.WriteLine("Invoking jQuery");
await jsRuntime.InvokeVoidAsync("setH1", "Hello world!");
Console.WriteLine("Invoked JQuery");
await base.OnAfterRenderAsync(firstRender);
}
The myJQueryTest.js
is simple:
window.setH1 = function (message) {
$('h1').text(message);
}
Demo repo created: https://github.com/conficient/BlazorDynamicScriptLoad
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With