Blazor — взаимодействие с JavaScript — не установлен диспетчер вызовов .NET

#c# #blazor

#c# #blazor

Вопрос:

Я экспериментирую с приложением Blazor WebAssembly. Когда моя страница (т. е. index.html ) загружается, я хочу передать массив JavaScript в приложение Blazor при его загрузке. При попытке вызвать метод из JavaScript я столкнулся с ошибкой, которая гласит:

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

Мой код выглядит так:

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;
    }
}
  

Первое отличие, которое я заметил, заключалось в том, что пример, показанный в документах, основывался на static методе. Это требование? Если это так, это будет означать, что, например, нет способа выполнить ведение журнала. Выход из системы, даже если я добавлю static к LoadItems методу, я все равно получаю ошибку, указанную выше. Я не понимаю, почему.

Короче говоря, я пытаюсь создать «безголовое» приложение Blazor. Я хотел бы использовать возможности C # для работы с данными, мне нужно передать результаты в пользовательский интерфейс, который использует HTML / CSS. Спасибо!

Ответ №1:

Добро пожаловать в Blazor!

Во-первых, я бы посоветовал прочитать документы при вызове.Net из js

Способ включения скрипта в ваш index.html означает, что скрипт выполняется до загрузки вашего приложения wasm.

Чтобы решить проблему, рекомендуется использовать js interop в методе жизненного цикла OnAfterRenderAsync (документы жизненного цикла)

Поэтому, если вы обновите свой скрипт до чего-то вроде:

 <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>
  

Затем в вашем компоненте (страница также является компонентом) вы сможете сделать что-то вроде:

 @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");
    }
}
  

Чтобы ответить на ваше замечание об этом статическом вызове. Ваш пример работает со статическим вызовом метода, Blazor также поддерживает вызов метода экземпляра. Здесь вы найдете хороший пример вызова экземпляра

Наконец, я не рекомендую touch index.html слишком много. Создайте и ссылайтесь на отдельный файл .js или из .NET 5 RC1, вы можете использовать изоляцию Js

Ответ №2:

Есть способ убедиться, что Blazor инициализирован перед попыткой вызвать interop

Измените свой индекс, чтобы предотвратить автозагрузку Blazor

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

Затем вы можете самостоятельно загрузить Blazor и использовать обещание для выполнения вашего взаимодействия

 <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>
  

Комментарии:

1. Он работает в Windows, но не работает на Android.

Ответ №3:

// Ошибка, с которой я столкнулся

blazor.webassembly.js: 1 Не перехваченный (в обещании) Ошибка: диспетчер вызовов .NET не установлен. в m (blazor.webassembly.js: 1:2655) в h (blazor.webassembly.js:1:2548) на объекте.e.invokeMethodAsync (blazor.webassembly.js:1:3389) в HTMLDocument. (CropHelper.js:73:12)

// Решение

 <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>