2
votes

I am working on project with Blazor technology. I need sometimes to use some JS code, and I need to include diffirant js files with each page, and as I know the only way to do it is adding it using JS function and Blazor JS invoke. So what I did is: in _Host.razor

function addScriptFile(fileName){
    var script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", file);
    document.getElementById(divName).appendChild(script);
}

Then in each page (component):

@inject IJSRuntime Js;
@functions{
    protected override async void OnAfterRender()
    {
        await Js.InvokeAsync<object>("addScriptFile","~/js/myFile.js");
    }
}

It is working good, but the problem happening if the page has been reloaded. It throws an error System.InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being prerendered and the page has not yet loaded in the browser or because the circuit is currently disconnected. Components must wrap any JavaScript interop calls in conditional logic to ensure those interop calls are not attempted during prerendering or while the client is disconnected.

What I understand from this error, that I am trying to invoke some javascript code before render completed. So I have used IComponentContext to make sure that the server is connected. In my journey to fixing this issue, I have created a new Blazor project without any JS files, but it throws the same error on reloading page

I tried to make this:

@inject IComponentContext ComponentContext
@functions{
    protected override void OnAfterRender()
    {
        if(ComponentContext.IsConnected)
        {
            //adding scripts
        }
    }
}

How to make JS working with Blazor in suitable way ? And how to fix that error ?

1

1 Answers

3
votes

In your code sample, there are a couple of issues.

@inject IJSRuntime Js;

@functions {
    protected override async void OnAfterRender()
    {
        await Js.InvokeAsync<object>("addScriptFile","~/js/myFile.js");
    }
}

Firstly, you should use code not functions. Code is specific to Blazor components, functions is used for Razor Pages and MVC.

Secondly, never use async void in Blazor, the exception to the rule would be to run async code in an event handler. See this article for more info.

Thirdly, you have the ~ character at the start of your script location. This character doesn’t work with Blazor (and is pretty pointless anyway in my opinion), it’s only valid with MVC and Razor Pages. If you want to load a script from the root of your application then just use a /, if you want to load it from a relative location then don’t prefix the location with anything.

So with all that said, I think your code should look like this.

@inject IJSRuntime Js;
@inject IComponentContext ComponentContext

@code {
    protected override async Task OnAfterRenderAsync()
    {
        if (!ComponentContext.IsConnected)
        {
            return;
        }

        await Js.InvokeAsync<object>("addScriptFile","/js/myFile.js");
    }
}