Working with the Blazor DynamicComponent

In this post, we discuss how to dynamically render components in Blazor using the new DynamicComponent.

Dave Brock
Dave Brock

Blazor is a joy when you know what you’re rendering—this typically involves knowing your types at compile time. But what happens when you want to render your components dynamically, when you don’t know your types ahead of time?

You can do it in a variety of ways: you can iterate through components and use complex conditionals, use reflection, declare a bunch of RenderFragments, or even build your own render tree. It can get complicated when dealing with parameters and complex data graphs, and none of these solutions are any good, really.

With .NET 6 Preview 1, the ASP.NET Core team introduced a built-in Blazor component, DynamicComponent, that allows you to render a component specified by type. When you bring in the component, you specify the Type and optionally a dictionary of Parameters.

You would declare the component like this:

<DynamicComponent Type="@myType" Parameters="@myParameterDictionary" />

The DynamicComponent has a variety of applications. I find it to be valuable when working with form data. For example, you can render data based on a selected value without having to iterate through possible types.

In this post, I’ll walk through how to use the DynamicComponent when a user selects a list from a drop-down list. I’ve got the repo out on GitHub for reference.

(Also, a shout-out to Hasan Habib’s nice video on the topic, which helped me think through some scenarios.)

Our use case

For a quick use case, I’m using a silly loan application. I want to gather details about where someone lives now (to determine how much they pay and whatnot). The drop-down has five possible values, and I’ve got some custom components in my Shared directory that render based on what a user selects.

Generate drop-down

To generate my drop-down list, I’ll use my component name as the option value. Here, I can use the nameof keyword, which returns component names as constant strings.

Here’s the markup of Index.cshtml so far:

@page "/"

<h1>Loan application</h1>

<div class="col-4">
    What is your current living situation?
    <select class="form-control">
        <option value="@nameof(DefaultDropdownComponent)">Select a value</option>
        <option value="@nameof(RentComponent)">Rent</option>
        <option value="@nameof(OwnHouseComponent)">Own house</option>
        <option value="@nameof(OwnCondoComponent)">Own condo or townhouse</option>
        <option value="@nameof(DaveRoommate)">I'm Dave's roommate</option>
    </select>
</div>

Write change event

We now need to decide what to do when a drop-down value is selected. To get the drop-down value, we work with the ChangeEventArgs type to get the value of the raised event—in this case, it’s the changed drop-down selection.

Before DynamicComponent came along, this is where you’d have logic to determine which component to render. Maybe you’d have a boolean flag, then use that in Razor markup. Instead, use Type.GetType to get the specific component to render.

Type selectedType = typeof(DefaultDropdownComponent);

public void OnDropdownChange(ChangeEventArgs myArgs)
{
  selectedType = Type.GetType($"DynamicComponentDemo.Shared.{myArgs.Value}");
}

Once I’m done with that, I can bind the drop-down’s onchange event to my OnDropdownChange method. Whenever the drop-down value changes, the method will trigger and determine which type it needs.

<select @onchange="OnDropdownChange" class="form-control">
  <option value="@nameof(DefaultDropdownComponent)">Select a value</option>
  <option value="@nameof(RentComponent)">Rent</option>
  <option value="@nameof(OwnHouseComponent)">Own house</option>
  <option value="@nameof(OwnCondoComponent)">Own condo or townhouse</option>
  <option value="@nameof(DaveRoommate)">I'm Dave's roommate</option>
</select>

Finally, I can render my DynamicComponent. I’ll place it right under my drop-down list.

<DynamicComponent Type="selectedType" />

Now we can see the page change using my DynamicComponent.

Optional: Pass in parameters

If your components have parameters, you can optionally pass them into your DynamicComponent. It takes a Dictionary<string, object>. The string is the name of your parameter, and the object is its value.

As a quick example, I can define ComponentMetadata through a quick class:

class ComponentMetadata
{
  public Type ComponentType { get; set; }
  public Dictionary<string, object> ComponentParameters { get; set; }
}

Then, I can create a dictionary for my components like this (only one component has a parameter):

private Dictionary<string, ComponentMetadata> paramsDictionaries = new()
{
  {
    "DefaultDropdownComponent",
    new ComponentMetadata { ComponentType = typeof(DefaultDropdownComponent)}
  },
  {
    "RentComponent",
    new ComponentMetadata { ComponentType = typeof(RentComponent)}
  },
  {
    "OwnCondoComponent",
     new ComponentMetadata { ComponentType = typeof(OwnCondoComponent)}
  },
  {
    "OwnHouseComponent",
    new ComponentMetadata { ComponentType = typeof(OwnCondoComponent)}
  },
  {
    "DaveRoommate",
    new ComponentMetadata
    {
      ComponentType = typeof(OwnCondoComponent),
      ComponentParameters = new Dictionary<string, object>()
      {
        { "CustomText", "Ooh, no." }
      }
    }
  }
};

Then, I could have logic that filters and passes in a ComponentParameters instance to the DynamicComponent, depending on what type I’m passing in. There’s a lot of power here—you could pass in data from an API or a database as well or even a function, as long as it returns a Dictionary<string, object>.

You might be asking: Why not use the catch-all approach?

<DynamicComponent Type="@myType" MyParameter="Hello" MySecondParameter="Hello again" />.

According to Blazor architect Steve Sanderson, he says:

If we do catch-all parameters, then every explicit parameter on DynamicComponent itself - now and in the future - effectively becomes a reserved word that you can’t pass to a dynamic child. It would become a breaking change to add any new parameters to DynamicComponent as they would start shadowing child component parameters that happen to have the same name … It’s unlikely that the call site knows of some fixed set of parameter names to pass to all possible dynamic children. So it’s going to be far more common to want to pass a dictionary.

Wrap up

In this post, we walked through the new DynamicComponent, which allows you to render components when you don’t know your types at runtime. We were able to render a component based on what a user selects from a drop-down list. We also explored how to pass in parameters to the DynamicComponent as well.

References

BlazorASP.NET Core