简体   繁体   中英

Blazor - JavaScript Interop - No .NET call dispatcher has been set

I am experimenting with a Blazor WebAssembly app . When my page (ie index.html) loads, I want to pass a JavaScript array to the Blazor app when it loads. While trying to call a method from JavaScript , I've run into an error that says:

Uncaught (in promise) Error: No .NET call dispatcher has been set

My code looks like this:

index.html

<body>
    <app>Loading...</app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>

    <script>
        let items = [
            { name:'Item 1', description:'This is a description of the first item' },
            { name:'Item 2', description:'This is a description of the second item' },
            { name:'Item 3', description:'This is a description of the third item' },
        ];

        try
        {
            DotNet
                .invokeMethodAsync('MyOrg.MyApp.Index', 'LoadItems')
                .then(results => {
                    console.log(results);
                })
            ;
        }
        catch (ex)
        {
            console.log(ex);
        }
    </script>
</body>

Index.razor

@page "/"

@using System.Threading.Tasks;    
@using Microsoft.Extensions.Logging;

@inject ILogger<Index> logger;

<p>
    Items loaded: <span>@items.Count</span>
</p>

@code {
    List<object> items = new List<object>();

    [JSInvokable]
    private Task LoadSnippets() 
    {
        try 
        {
            logger.LogInformation("Loading items...");
            items = new List<object>();
        }
        catch (Exception ex)
        {
            logger.LogError(ex, $"Failed to load items.");
        }
        return Task.CompletedTask;
    }
}

The first difference I noticed was that the example shown in the docs relied on a static method. Is this a requirement? If so, that would imply there's not a way to perform logging for example. Logging aside, even if I add static to the LoadItems method, I still get the error listed above. I don't understand why.

In short, I'm trying to create a "headless" Blazor app. I'd like to use the richness of C# to work with the data, I need to pass the results to the UI, which relies on HTML/CSS. Thank you!

Welcome to Blazor!

Firstly, I would suggest reading the docs at Call .Net from js

The way you include the script in your index.html means, that the script is executed before your wasm app is loaded.

To solve the issue, it is a good practice to use js interop in OnAfterRenderAsync lifecycle method ( Lifecycle docs )

So if you update your script to something like:

<script>
        netFromJs = {
            staticCall:  function () {
                let items = [
                    { name: 'Item 1', description: 'This is a description of the first item' },
                    { name: 'Item 2', description: 'This is a description of the second item' },
                    { name: 'Item 3', description: 'This is a description of the third item' },
                ];

                try {
                    DotNet
                        .invokeMethodAsync('wasmprerender.Client', 'LoadItems')
                        .then(results => {
                        console.log(results);
                    });
                }
                catch (ex) {
                    console.log(ex);
                }
            }
        }
    </script>

Then in your component (page is also component), you will be able to do something like:

@inject IJSRuntime _js

@code {
    static List<object> items = new List<object>();

    [JSInvokable]
    public static Task LoadItems()
    {
        try
        {
            Console.WriteLine("Loading items");
            items = new List<object>();
        }
        catch (Exception ex)
        {
        }

        return Task.CompletedTask;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await _js.InvokeVoidAsync("netFromJs.static");
    }
}

To address your note about that the static call. Your example works with the Static method call, Blazor also supports the Instance method call. You will find a nice example here Instance call

Lastly, I do not recommend touch index.html too much. Create and reference separate .js file or from .NET 5 RC1 you can use Js isolation

There's a way to ensure Blazor is initialized before trying to call interop

Modify your index to prevent Blazor autoloading

<script src="_framework/blazor.webassembly.js" autoload="false"></script>

Then you can boot Blazor yourself and use a promise to execute your interop

<script>
    let items = [
        { name:'Item 1', description:'This is a description of the first item' },
        { name:'Item 2', description:'This is a description of the second item' },
        { name:'Item 3', description:'This is a description of the third item' },
    ];
    window.Blazor.start()
       .then(()=>DotNet.invokeMethodAsync('MyOrg.MyApp.Index', 'LoadItems'))
       .then(results => console.log(results))
       .catch(ex=>console.log(ex));
</script>

// Error I faced

blazor.webassembly.js:1 Uncaught (in promise) Error: No .NET call dispatcher has been set. at m (blazor.webassembly.js:1:2655) at h (blazor.webassembly.js:1:2548) at Object.e.invokeMethodAsync (blazor.webassembly.js:1:3389) at HTMLDocument. (CropHelper.js:73:12)

// Solution

<script>
    window.Blazor.start()          
        .then(function () {
            var fileref = document.createElement('script')
            fileref.setAttribute("type", "text/javascript")
            fileref.setAttribute("src", "js/CropHelper.js")
            if (typeof fileref != "undefined")
                document.getElementsByTagName("head")[0].appendChild(fileref)
        })
        .then(results => console.log(results))
        .catch(ex => console.log(ex));

</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM