Andres Talavera
Andres Talavera

Reputation: 2200

Call JavaScript function (from external library) with Blazor

I try to call a JS function. The JS script is provided by a third party.

In simple HTML/JS, I can write:

<html>
    <head>
        <script src="myscriptA.js"></script>
        <script src="myscriptB.js"></script>
    </head>
    <body>
        <div id="foo"></div>
        <div id="bar"></div>
        <script>
            var viewer = new foo.Viewer('foo', /*other params*/);
            viewer.function1().then(() => { /*ommited code*/ }).catch((error) => { console.log(error)});
            document.getElementById('bar').addEventListener('click', event => {
                var b1 = new B('bar', /*params*/)});
                var b2 = new B('foo');
            viewer.addB(b1, b2, { click: function(e) { viewer.function2() } });
        </script>
    </body>
</html>

I want to do the same thing with Blazor. For the moment, I can reach the myscriptA.js file with my component:

Page3.razor

@page "/Page3"
@inject IJSRuntime JS
@code {
    var scriptA = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/myscriptA.js");
    var foo = await JSRuntime.InvokeAsync<IJSObjectReference>("new foo.Viewer", scriptA);
    // I have try:
    var foo = await JSRuntime.InvokeAsync<IJSObjectReference>("new foo.Viewer", "foo", @*other params*@);
    var foo = await JSRuntime.InvokeAsync<IJSObjectReference>("foo.Viewer", "foo", @*other params*@);
    var foo = await JSRuntime.InvokeAsync<IJSObjectReference>("Viewer", "foo", @*other params*@);
    var foo = await JSRuntime.InvokeAsync<IJSObjectReference>("new Viewer", "foo", @*other params*@);
}

For each C# foo variable in my component, my browser's console shows:

blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not find 'new foo.Viewer' ('new foo' was undefined).
      Error: Could not find 'new foo.Viewer' ('new foo' was undefined).
          at https://localhost:5001/_framework/blazor.webassembly.js:1:1287
          at Array.forEach (<anonymous>)
          at e.findFunction (https://localhost:5001/_framework/blazor.webassembly.js:1:1247)
          at b (https://localhost:5001/_framework/blazor.webassembly.js:1:2989)
          at https://localhost:5001/_framework/blazor.webassembly.js:1:3935
          at new Promise (<anonymous>)
          at Object.beginInvokeJSFromDotNet (https://localhost:5001/_framework/blazor.webassembly.js:1:3908)
          at Object.w [as invokeJSFromDotNet] (https://localhost:5001/_framework/blazor.webassembly.js:1:64232)
          at _mono_wasm_invoke_js_blazor (https://localhost:5001/_framework/dotnet.5.0.4.js:1:190800)
          at do_icall (<anonymous>:wasm-function[10596]:0x194e4e)

Updates

May 8th, 2021: index.html

As written in the comment (Call JavaScript function (from external library) with Blazor), I want to inject myscriptA.js and myscriptB.js only in Page3.razor file and not in another files (Page1, Page2, Page4, ...).

May 11th, 2021: js file

As *.css files for Blazor components, I tried to add a *.js file with the same name as my page:

I've the same error

Upvotes: 3

Views: 8657

Answers (2)

Cory Podojil
Cory Podojil

Reputation: 854

You were close!! To load a JS file only where required (e.g., a specific page), you need to implement the IJSObjectReference interface and utilize the JavaScript module isolation properly.

Razor page:

@inject IJSRuntime JsRuntime

<h1>My Page</h1>

@code{
private IJSObjectReference MyJsModule { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        try
        {
            // Load the JS Helpers Module
            MyJsModule = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/MyScript.js");
        }
        catch (Exception ex)
        {
            Logger.LogError($"Failed to load JS module. Error: {ex}");
        }
    }
}

private async void AddViewer()
{
    try
    {
        await MyJsModule.InvokeVoidAsync("AddViewer", "foo");
    }
    catch (Exception ex)
    {
        Logger.LogError($"Failed to execute JS function. Error: {ex}");
    }
}

MyScript.js file:

import "./myScriptWithUsefulCodes.min.js";

export function AddViewer(id) {
    var viewer = new foo.Viewer(id, /*other params*/);
    /* ...Your code... */
}

Read more here: https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-5.0#javascript-isolation-in-javascript-modules

Upvotes: 11

Stef Heyenrath
Stef Heyenrath

Reputation: 9830

Couldn't you just load the JavaScript file in your Blazor Page, the same way as the default Blazor example loads the .json file?

And then 'execute' the contents from that string as JavaScript using the IJSRuntime ?

Upvotes: 0

Related Questions