JSInterop is not add the script before OnAfterRenderAsync

Enrico Rossini 201 Reputation points
2022-02-23T11:45:46.96+00:00

I have a component for Blazor. I have a JSInterop that is defined like that

public class JSMarkdownInterop
{
    IJSRuntime jsRuntime;

    public JSMarkdownInterop(IJSRuntime JSRuntime)
    {
        jsRuntime = JSRuntime;

        jsRuntime.InvokeAsync<IJSObjectReference>("import",
            "/_content/PSC.Blazor.Components.MarkdownEditor/js/markdownEditor.js");
    }

    public async ValueTask AddJS(string targetUrl)
    {
        await jsRuntime.InvokeVoidAsync("loadJs", targetUrl);
    }
}

The function loadJS is in the markdownEditor.js. So, in the component I added this code in the OnInitialized function

protected JSMarkdownInterop JSModule { get; private set; }

protected override void OnInitialized()
{
    if (JSModule == null)
        JSModule = new JSMarkdownInterop(JSRuntime);

    base.OnInitialized();
}

then, in the OnAfterRenderAsync I want to call the AddJS function to add some scripts.

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    await base.OnAfterRenderAsync(firstRender);

    if (firstRender)
    {
        await JSModule.AddJS(easyMDEJS);
    }
}

I end up to have an error because apparently the fucntion loadJS was undefined.

enter image description here

How you can see in the above screenshot, there is the error but also the application added the script. Then, I think the application adds the script after the first render.

How can I delay the first render until the script is added? Or how can I add the script on time for the first render?

Update

I changed the JSMarkdownInterop like that

public class JSMarkdownInterop : IAsyncDisposable
{
    private readonly Lazy<Task<IJSObjectReference>> moduleTask;

    public JSMarkdownInterop(IJSRuntime jsRuntime)
    {
        moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>("import",
            "./_content/PSC.Blazor.Components.MarkdownEditor/js/markdownEditor.js")
            .AsTask());
    }

    public async ValueTask AddJS(string targetUrl)
    {
        var module = await moduleTask.Value;
        await module.InvokeVoidAsync("loadJs", targetUrl);
    }
}

but I receive the same error.

Developer technologies .NET Blazor
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2022-02-23T16:55:03.757+00:00

    you are probably calling the function before the script has loaded

    you don't show the javascript load() function source. but it probably add a script tag to the dom. unlike at render time, this is async. the load() function should return a promise that is resolved by the script load event.


  2. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2022-02-23T18:21:26.767+00:00

    the docs recommend adding the scripts to the index.html.

    your component can dynamically load the script, but you need to write the load() in javascript and include on the index.html page. you would also want the load() function to check if the script is loaded, so I'd use an id on the script tag. something like:

    const loadScript = function (name,url) {
        if (document.getElementById(name)) 
            return;
    
        return new Promise(function (resolve, reject) {
            const script = document.createElement('script');
            script.src = url;
            script.id = name;
    
            script.addEventListener('load', function () {
                // The script is loaded completely
                resolve(true);
            });
    
            document.head.appendChild(script);
        });
    };
    

  3. Bruce (SqlWork.com) 77,686 Reputation points Volunteer Moderator
    2022-03-07T16:50:17.55+00:00

    you should add a module load library in index.html. you might want to use require.js:

    https://requirejs.org/docs/commonjs.html

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.