public class Blazor in WordPress – Multiple Components

{

In my previous post, I showed how to simply embed a single Blazor Component inside a WordPress page or post. However, what I really want is the chance to willy-nilly throw in as many components as I want, wherever I want. This runs into some problems. If you try to just copy/paste my code from the first post multiple times, and run it with your dev tools open, you’ll see multiple errors.

 

The first issue is with the line <script src="_framework/blazor.webassembly.js"></script>. As with any javascript file, you should only import blazor.webassembly one time, not multiple times. Sure, I could remember just to do that the first time, but it would be even nicer to have it built into my WordPress blog, and never have to worry about adding that line. It’s also more “correct” for websites to import scripts and other files in the head or at the bottom of the body (footer area), rather than in the midst of the content.

 

If this were a custom website, I would just add the tag to index.html. But with WordPress, there’s lots of warnings about interfering scripts, loading order, and so forth, so they don’t recommend you do this yourself. Instead, I installed a plugin, Header and Footer Scripts, which gives you WP UI-level control over custom tags in a header and/or footer.

 

After installing the plugins, I moved the blazor.webassembly script to the header, and reloaded to make sure everthing still worked. I discovered that I also had to move <base href="/" /> to make it still work. But again, this is something you would only want to do once per page. (And, side note, it’s the biggest failing of this whole setup, as redirecting all url calls can interfere with other WP plugins.)

 

Finally, if we’re going to move scripts, we should also move any stylesheet link tags. With all those moved out of the way, now the in-post html looks much more concise and neat:

 

<div id="app">Loading...</div>

 

Great! But now…I want two different components. OK, so, obviously, I should replace the id="app" with some other ids. Let’s look again at what the reference looks like in our Blazor Web Assembly Program.cs file:

 

public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");

builder.Services.AddScoped(
sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)});

await builder.Build().RunAsync();
}

[Program.Main]

 

We already mentioned in the last post that you can replace <App> and "#app" with your own razor component file and id tag. So I tried adding two.

 

builder.RootComponents.Add<DataGrid>("#grid");
builder.RootComponents.Add<MusicKeyboard>("#keyboard");

 

And this definitely worked with two Id’ed div elements in a page! Unfortunately…if you happen to only want to use one of your two components, then Blazor throws an error that it can’t find the matching div for the other one!

 

My first attempt to fix this was to revert back to just loading the App razor component, and using logic inside that to decide which other component to show. This turned out to work for single components, but Blazor would get confused if it saw two "app" divs!

 

Luckily, after some more Google-fu, I stumbled on this GitHub issue: Unable to use RootComponents twice on same page, where the original poster, Ecroyd, came up with a clever workaround of using a custom element tag (type) for each component, looking up which components are on the page with JSInterop, and then only adding the RootComponents that are found!

 

You can see Ecroyd’s version on the link above, but I did some more tweaking. I decided I would rather keep these div elements, and instead add a class="blazor-component" to identify each.

 

var jsRuntime = builder.Services.BuildServiceProvider().GetRequiredService<IJSRuntime>();
var module = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "/componentFinder.js");
var json = await module.InvokeAsync<JsonElement>("getBlazorComponents");
foreach(var id in json.EnumerateArray())
{
switch (id.ToString())
{
case "blazor-data-grid":
builder.RootComponents.Add<BdGridDemo>("#blazor-data-grid");
break;
case "blazor-music-keyboard":
builder.RootComponents.Add<MusicKeyboardDemo>("#blazor-music-keyboard");
break;
}
}

[Program.Main changed code]

 

export function getBlazorComponents() {
return Array.from(document.querySelectorAll('.blazor-component')).map(a => a.id);
}

[componentFinder.js lookup module]

 

Now, for each component I want to embed, I add the following (some styling added for layout):

 

<div class="blazor-component" style="height: 550px; background: white; padding: 10px; overflow-x: scroll;" id="blazor-music-keyboard">Loading Blazor Music Keyboard ... </div>

 

 

Loading Blazor Music Keyboard …

 

Or

 

<div class="blazor-component" style="height: 800px; overflow-y: scroll; width: 100%; background: white; color: black; padding: 20px; font-family: arial;" id="blazor-data-grid">Loading Blazor Data Grid ... </div>

 

 

Loading Blazor Data Grid …
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.