Blazor включить динамические корневые компоненты / Ошибка: Динамические корневые компоненты не были включены в этом приложении

#javascript #c# #blazor

Вопрос:

Я хочу визуализировать компонент Blazor из javascript. Видишь https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-rc-1/ «Визуализация компонентов Blazor из JavaScript»

У меня есть HTML-файл:

 <script src="/_framework/blazor.server.js"></script>
<div id="counter"></div>

<script>
    async function ready() {
        let containerElement = document.getElementById('counter');
        await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });
    }
    document.addEventListener("DOMContentLoaded", ready);
</script>
 

И сделать

 builder.Services.AddServerSideBlazor(options =>
{
    options.RootComponents.RegisterForJavaScript<Counter>("counter");
});
 

Сообщение об ошибке (JavaScript):

 test.html:14 Uncaught (in promise) Error: Dynamic root components have not been enabled in this application.
    at E (blazor.server.js:1)
    at Object.add (blazor.server.js:1)
    at HTMLDocument.ready (test.html:8)
 

Как я могу включить динамические корневые компоненты?

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

1. Я тоже сталкиваюсь с этим. Я постараюсь поделиться этим, чтобы узнать, может ли кто-нибудь нам помочь. На случай, если кому-то понадобится образец, это для Blazor WASM, но та же ошибка: github.com/Swimburger/RenderBlazorFromJs

Ответ №1:

Ошибка происходит из-за задержки между загрузкой документа и тем, что Blazor «готов» обработать ваш запрос на добавление компонента.

Похоже, для этого нет никакого официального документированного решения, но что-то подобное возможно

Переключите Blazor на ручной запуск:

index.html

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

Запустите Blazor, затем попробуйте добавить компоненты — повторяйте до тех пор, пока не добьетесь успеха

script.js

 document.addEventListener("DOMContentLoaded", startBlazor)

function startBlazor() {
    Blazor
        .start()
        .then(() => {
            requestAnimationFrame(AddBlazorComponents)
        })
        .catch((error) => console.error(error));
}

let attempts = 0
const maxAttempts = 120 // arbitrary choice

function AddBlazorComponents() {
    if (attempts > maxAttempts) {
        // give up after trying too many times
        console.error("Could not load Blazor components.")
        return
    }

    const containerElement = document.querySelector('#app')
    Blazor.rootComponents.add(containerElement, 'app', {})
        .catch(reason => {
            if (reason.message === "Dynamic root components have not been enabled in this application.")
                requestAnimationFrame(AddBlazorComponents)
            else
                console.error(reason)
        })
    attempts  
}
 

Лучшее решение?

Возможно, вы могли бы добавить вызов JsInterop в свой .СЕТЕВОЙ код для облегчения этого после запуска приложения — но он будет отличаться для разных моделей хостинга : сервер Blazor / веб-сборка Blazor.

Еще лучше было бы сделать PR в репо aspnetcore с помощью метода предварительной регистрации компонентов для загрузки фреймворка, когда он будет готов.

Обновление от 29 сентября 2021 года

Оказывается, в настоящее время существует некоторая недокументированная функциональность, которая помогает здесь. Я скопировал свой комментарий из выпуска @Swimburger на github ниже:

Новый инициализатор JavaScript afterStarted вызывается (потенциально) слишком рано для динамических компонентов, но оказывается, что вы можете передать инициализатор для вызова динамического компонента, но он недостаточно хорошо документирован или интуитивно понятен imho.

Чтобы это сработало, я изменил запуск приложения следующим образом (добавив javaScriptInitializer: "loadApp" ) в program.cs:

builder.RootComponents.RegisterForJavaScript<App>(identifier: "app", javaScriptInitializer: "loadApp");

Затем, и именно здесь это было неинтуитивно, я добавил экспорт в свой модуль (например afterStarted ) под названием loadApp — но он не был вызван.

Оказалось, что loadApp это можно найти, только если я добавлю его в window объект, поэтому я добавил это в index.html

 <script src="_framework/blazor.webassembly.js"></script>
<script>
    window.loadApp = function (component,params) {
        let containerElement = document.querySelector('#app')
        window.Blazor.rootComponents.add(containerElement, component, params)
    }
</script>
 

Такое ощущение, что мне чего-то не хватает в том, как экспортировать свой пользовательский инициализатор, или его действительно нужно добавить window напрямую, что кажется странным…

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

1. Когда я пытаюсь это сделать, мне не нужно запрашивать логику анимационного кадра, чтобы продолжать попытки. Это всегда срабатывает с первой попытки. gist.github.com/Swimburger/c808f10bcb2c022c624aca233beecca1 Спасибо вам за это решение!

2. Я также создал проблему на GitHub, чтобы посмотреть, сможем ли мы сделать это более интуитивно понятным: github.com/dotnet/aspnetcore/issues/37074

3. @Swimburger Я думаю, что вам повезло или, возможно, вы использовали другой браузер — я на самом деле написал/протестировал это с помощью вашего репозитория github и определенно нуждался в логике повторных попыток — обычно около 2 или 3 попыток, прежде чем он был готов.

4. Странно, он всегда загружается правильно каждый раз, когда я его пробую. Я попробовал это в Chrome, Edge и Firefox. Я еще не пробовал на Сафари. Я также попробовал с медленной эмуляцией 3G в Chrome и без проблем. Не могли бы вы предоставить более подробную информацию о вашем браузере?

5. Я использовал Edge/Chrome/Firefox — я добавил журнал консоли в AddBlazorКомпоненты и увидел, что он регистрировался 2/3 раза