Dynamic Razor Components

Dynamic Razor Components

One of the things I’m playing around with is the possibility of dynamically generating a Blazor form based on nothing more than an object. So, for instance, if an object has a couple of string properties then I’d like a form with a couple of textboxes, for editing those properties at runtime.

The idea is to generate the form based solely on the object structure and any associated meta-data – for instance, I can see decorating the properties, on the object’s class, to provide hints about how I want the resulting form to look.

The main goals would be repeatability (the same object should generate the same form, over and over), reliability (all reasonable combinations of property types should be covered, so nothing breaks the form generator), and overall flexibility (don’t forbid a bunch of property types or form/component options just because they are harder to generate).

Anyway, it’s a work in progress.

I did learn something interesting though, while doing research for this idea. There are 50 bazillion examples, on the Interwebz, of dynamically generating a control, in Blazor, like this:

@page "/"
@using Microsoft.AspNetCore.Components.Rendering

@RenderFragment

@code {
    RenderFragment RenderFragment {get; set;}

    protected override void OnInitialized()
    {
        RenderFragment = (builder) =>
        {
            var index = 0;
            builder.OpenComponent(index++, typeof(MudButton));
            builder.AddAttribute(index++, "Variant", Variant.Filled);
            builder.CloseComponent();
        };

        base.OnInitialized();
    }    
}

Use the OpenComponent call to render the form component , use the AddAttribute to add any attributes, then use the CloseComponent call to close the HTML tag.

That works great, by the way, as long as the HTML tag in question doesn’t need anything in the inner HTML. For instance, in my example above I’m trying to generate a MudBlazor button. But, the label for a MudBlazor button is specified with child content, not with an attribute.

So, how the heck do I do that??

I checked the Interwebz. All I heard was the sound of crickets. Then, I discovered a blog entry on the Syncfusion website, HERE. That led me to this solution:

@page "/"
@using Microsoft.AspNetCore.Components.Rendering

@RenderFragment

@code {
    RenderFragment RenderFragment {get; set;}

    protected override void OnInitialized()
    {
        RenderFragment = (builder) =>
        {
            var index = 0;
            builder.OpenComponent(index++, typeof(MudButton));
            builder.AddAttribute(index++, "Variant", Variant.Filled);
            builder.AddAttribute(
               index++, 
               "ChildContent", 
               (RenderFragment)((builder2) =>
               {
                   builder2.AddContent(index++, "Push Me!");
               }));
            builder.CloseComponent();
        };

        base.OnInitialized();
    }    
}

See that extra AddAttribute call? Yeah, that’s important. That creates a second (child)RenderTreeBuilder instance and uses it to add the missing child content.

It turns out, child content is actually an attribute called “ChildContent”.

Who knew?

Obviously not me.

The result is a pretty MudBlazor button with a “Push Me!” label:

I just thought that little nugget was worth writing about. For myself if nobody else. That way, in case I get pulled away from my idea for weeks / months, I won’t forget all this again.

Photo by Scott Graham on Unsplash