Dynamic Component Rendering in Blazor 8

01/19/2024

Dynamic Component Rendering in Blazor 8

How to make multiple Blazor components work seamlessly on the same page

We're working on a settings page in our Blazor 8 app, and we've been building all sorts of components for it. These configuration pages have a lot going on, and as a result are extremely stateful, running on the server, making everything very dynamic - and creates problems when rendering, when we're not careful.

We've got a Syncfusion Sidebar with a TreeView inside of it, and what we want is for a control to render or show itself when a tree item is selected. These controls have events and parameters in them. The parameters are populated by a parent class to the rendering page, and the events go into the page to handle the events accordingly. Pretty standard stuff.

Trying to have all of these controls together on the same page, and presumably at the same time, proposes all sorts of challenges that we couldn't initially seem to get around. At first, we thought that by settting a Visible property on the controls would let us switch between what gets rendered and handled, with relative ease.

Yeah, don't do that. Quite simply, it doesn't work the way you think it does. You have to do it 'the hard way'. It's not so bad - here's how it's done.

Separate code files

Might be a no-brainer to many of you, but to the uninitiated, having your code in a separate file is a huge help. You should have something that looks like this:

separate files

Note the file name.

Your code file will be a partial class that looks something like this:

public partial class OwnerSettingsPage : SomeBaseClass
{
    ...
}

Jetbrains Rider will still autogenerate code in your razor file, but that's okay - you simply move it in here if that happens.

Your Dynamic component

In your Razor page, where the components will go, you will set this up:

<div class="md:w-1/2 lg:w-1/2 mx-3">
    <SfSpinner @bind-Visible="spinVisible" Type="SpinnerType.Tailwind"></SfSpinner>
    @dynamicComponent
</div>

The spinner is optional.

In your code file, you'll have that variable declared as such:

private RenderFragment dynamicComponent;

Configure Component Events

If your component generates events, you'll need to pass some kind of handler to them.

In our code we have a BusinessInfo component that emits an OnSave event, and an OnSavePlace event with a parameter of type PlaceResult which we're getting from the very good BlazorGoogleMaps library. When we collect address information from the component, the page needs to handle it.

Here's what that looks like:

private EventCallback<PlaceResult> saveBusinessPlace;
private EventCallback saveBusiness;

protected override async Task OnInitializedAsync()
{
    saveBusiness = EventCallback.Factory.Create(this, SaveBusinessInfo);    
    saveBusinessPlace = EventCallback.Factory.Create<PlaceResult>(this, SaveBusinessPlace);
}

Here we're connecting the EventCallback to the SaveBusinessPlace method in our page, which takes the PlaceResult object as a variable.

Build the Component

In our code we have a method called buildBusinessInfo which builds the component for us, like so:

    private void buildBusinessInfo()
    {
        dynamicComponent = builder =>
        {
            builder.OpenComponent(0, typeof(BusinessInfoTab));
            builder.AddAttribute(0, "OnSave", saveBusiness);
            builder.AddAttribute(1, "OnSavePlace", saveBusinessPlace);
            builder.AddAttribute(2,"Business",svc.Business);
            builder.CloseComponent();
        };
    }

A couple of things to note here:

  • the parameters we pass in to the component in AddAttribute are of no particular order
  • the names (in quotes) match the parameter names of the component
  • we have a Business object that we pass in as well
  • we start things with an OpenComponent and end things with a CloseComponent, because the builder is essentially writing out the tags into the dynamicComponent before the page before rendering

Not going to show the code, but in the ListView we have an event that gets generated when someone clicks on a node, and when they do the buildBusinessInfo method is called, and the component is rendered.

All in all it's not a lot of work, but it is a bit of code and plumbing to put in place. The good news is, it works really well and makes things look pretty seamless on our end. Granted, it's still a webpage and there's still plenty of other things that need to go in place, but this helped us answer a tough question on how to support multiple screen settings without having to make a lot of redundant pages.

Acknowledgements

I would like to thank the mighty JetBrains AI for helping us come up with this answer. I wasn't sure what to make of an AI product in development but their tool is turning out to be a tremendous help, especially for Blazor development, and I can't recommend it strongly enough.


Looking for help upgrading your web apps to Blazor? Contact us and let's see what we can do to help!