Blazor WebAssembly: dynamic creation of components based on JSON configuration

Alexey Boyko
CodeX
Published in
3 min readSep 10, 2021

The note describes a way to dynamically add components to a page using JSON configuration using ASP.NET Core 6.0 DynamicComponent (currently in preview state).

The dynamic creation of components can be used for form builders:

  • The form is configured by JSON;
  • Form elements (or components) are not limited to a predefined set. New components can be added. Also new components can be loaded from other dll libraries.

Look at a prototype of form builder: Blazor WebAssembly form builder demo.

Visual form builder built with Blazor WebAssembly
Visual form builder built with Blazor WebAssembly

Example of a component with an event — NumButton

Example of a component with a parameter and an event:

<button type="button" @onclick=Click>@Num</button>@code {
[Parameter]
public int Num { get; set; }
[Parameter]
public EventCallback<int> OnClick { get; set; }
async Task Click()
=> await OnClick.InvokeAsync(Num);
}

Listing 1. The NumButton Component.

NumButton standard, not dynamic, usage:

<NumButton Num=10 OnClick=Click />@code {  
async Task Click(int count)
=> await JsRuntime.InvokeVoidAsync("alert", count);
}

Listing 2. NumButton usage.

Dynamic creation of a component with event handler

NumButton can be created with DynamicComponent:

<DynamicComponent Type=ComponentType
Parameters=ComponentParameters />
@code {
Type ComponentType;
Dictionary<string, object> ComponentParameters;
protected override void OnInitialized() {
ComponentType = typeof(NumButton);
ComponentParameters = new Dictionary<string, object> {
{ "Num", 10}
};
}
async Task Click(int count)
=> await JsRuntime.InvokeVoidAsync("alert", count);
}

Listing 3. Creating NumButton with DynamicComponent. OnClick handler not set.

Nothing complicated, this is detailed in the DynamicComponent documentation. But the documentation does not describe how to subscribe to an event that triggers a dynamically created component. In the example, this is the “OnClick” event.

Use EventCallback.Factory to create an event handler:

<DynamicComponent Type=ComponentType
Parameters=ComponentParameters />
@code {
Type ComponentType;
Dictionary<string, object> ComponentParameters;
protected override void OnInitialized() {
ComponentType = typeof(NumButton);
ComponentParameters = new Dictionary<string, object> {
{ "Num", 10},
// event subscription
{ "OnClick",
EventCallback.Factory.Create<int>(this, Click)}
};
}
async Task Click(int count)
=> await JsRuntime.InvokeVoidAsync("alert", count);
}

Listing 4. Creating NumButton with DynamicComponent with OnClick handler.

Dynamic creation of a component based on JSON configuration

JSON configuration looks like that:

{
"TypeName": "FormDesignerDemo.Components.NumButton",
"Parameters": {
"Num": {
"TypeName": "System.Int32",
"Value": 10
}
}
}

Listing 5. JSON configuration of NumButton.

In this example, the “Num” parameter is of primitive type “System.Int32”. The parameter doesn’t have to be primitive, any serializable type can be used.

The following classes conform to this JSON format:

class ComponentDto {
public string TypeName { get; set; }
public Dictionary<string, ParameterValueDto> Parameters
{ get; set; }
}
class ParameterValueDto {
public string TypeName { get; set; }
public object Value { get; set; }
}

Listing 6. Data Transfer Object classes that describe dynamic components serializable to JSON.

Most likely, in a real project, you will not have to manually process JSON. In your Blazor app use this code to automatically deserialize:

await Http.GetFromJsonAsync<ComponentDto>(...)

Let’s create a description of a dynamic component:

class ComponentDescription {
public Type ComponentType { get; set; }
public Dictionary<string, object> Parameters { get; set; }
}

Listing 7. Description of a dynamic component.

And DTO to ComponentDescription conversion helper:

static class ComponentDtoToComponent {
public static ComponentDescription ToComponent(
this ComponentDto dto) {
return new ComponentDescription {
ComponentType = Type.GetType(dto.TypeName),
Parameters = dto.Parameters?.ToDictionary(
pp => pp.Key,
pp => pp.Value != null
? JsonSerializer.Deserialize(
((JsonElement)pp.Value.Value).GetRawText(),
Type.GetType(pp.Value.TypeName))
: null)
?? new Dictionary<string, object>()
};
}
}

Listing 8. DTO to ComponentDescription conversion helper.

Complete code:

<DynamicComponent Type=Comp.ComponentType
Parameters=Comp.Parameters />
@code {
ComponentDescription Comp;
protected override void OnInitialized() {
Comp = JsonSerializer.Deserialize<ComponentDto>(@"
{
""TypeName"":
""FormDesignerDemo.Components.NumButton"",
""Parameters"": {
""Num"": {
""TypeName"": ""System.Int32"",
""Value"": 10
}
}
}")
.ToComponent();
Comp.Parameters.Add("OnClick",
EventCallback.Factory.Create<int>(this, Click));
}
async Task Click(int count)
=> await JsRuntime.InvokeVoidAsync("alert", count);
}

Listing 9. Dynamic creation of components based on JSON configuration and setting up event handler.

--

--