Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor enable dynamic root components / Error: Dynamic root components have not been enabled in this application

I want render a Blazor component from javascript. See https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-rc-1/ "Render Blazor components from JavaScript"

I have a HTML file:

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

And do

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

Error message (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)

How can i enable dynamic root components?

like image 519
Xeddon Avatar asked Oct 24 '25 19:10

Xeddon


1 Answers

The error is happening because of the delay between the document loading and Blazor being "ready" to process your request to add a component.

There doesn't appear to be any official documented solution for this, but something like this is possible

Change Blazor to manual start:

index.html

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

Start Blazor, then try to add components - repeat until success

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

Better solution?

You could, possibly, add a JSInterop call to your .NET code to facilitate this, after the application starts - but it would be different for the different hosting models : Blazor Server / Blazor WebAssembly.

Even better might be to make a PR to the aspnetcore repo with a method of pre-registering components for the framework to load when it is ready.

Update 29 sep 2021

It turns out there is some currently undocumented functionality that helps here. I have copied my comment from @Swimburger 's github issue below:

The new JavaScript Initializer afterStarted is called (potentially) too soon for Dynamic components, but it turns out you can pass an initializer to be called for a dynamic component - but it is not well documented or intuitive imho.

To make this work, I changed the app startup like this (adding javaScriptInitializer: "loadApp") in program.cs:

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

Then, and this is where it was non-intuitive, I added an export to my module (like afterStarted) called loadApp - but it wasn't called.

It turned out that loadApp is only found if I add it to the window object, so I added this to 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>

This feels like I am missing something in how to export my custom initializer OR it really does need to be added to window directly, which seems odd...

like image 108
Mister Magoo Avatar answered Oct 26 '25 08:10

Mister Magoo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!