Bewerken

Share via


Dynamically-rendered ASP.NET Core Razor components

Note

This isn't the latest version of this article. For the current release, see the .NET 8 version of this article.

Warning

This version of ASP.NET Core is no longer supported. For more information, see .NET and .NET Core Support Policy. For the current release, see the .NET 8 version of this article.

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 8 version of this article.

By Dave Brock

Use the built-in DynamicComponent component to render components by type.

Dynamic components

A DynamicComponent is useful for rendering components without iterating through possible types or using conditional logic. For example, DynamicComponent can render a component based on a user selection from a dropdown list.

In the following example:

  • componentType specifies the type.
  • parameters specifies component parameters to pass to the componentType component.
<DynamicComponent Type="componentType" Parameters="parameters" />

@code {
    private Type componentType = ...;
    private IDictionary<string, object> parameters = ...;
}

For more information on passing parameter values, see the Pass parameters section later in this article.

Example

In the following example, a Razor component renders a component based on the user's selection from a dropdown list of four possible values.

User spaceflight carrier selection Shared Razor component to render
Rocket Lab® RocketLab.razor
SpaceX® SpaceX.razor
ULA® UnitedLaunchAlliance.razor
Virgin Galactic® VirginGalactic.razor

RocketLab.razor:

<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

SpaceX.razor:

<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of 
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

UnitedLaunchAlliance.razor:

<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

VirginGalactic.razor:

<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of 
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

DynamicComponent1.razor:

@page "/dynamic-component-1"

<PageTitle>Dynamic Component 1</PageTitle>

<h1>Dynamic Component Example 1</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var entry in components.Keys)
            {
                <option value="@entry">@entry</option>
            }
        </select>
    </label>
</p>

@if (selectedType is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedType" />
    </div>
}

@code {
    private readonly Dictionary<string, Type> components = new()
    {
        ["Rocket Lab"] = typeof(RocketLab),
        ["SpaceX"] = typeof(SpaceX),
        ["ULA"] = typeof(UnitedLaunchAlliance),
        ["Virgin Galactic"] = typeof(VirginGalactic)
    };
    private Type? selectedType;

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && 
            !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedType = components[dropdownValue];
        }
        else
        {
            selectedType = null;
        }
    }
}

RocketLab.razor:

<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

SpaceX.razor:

<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of 
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

UnitedLaunchAlliance.razor:

<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

VirginGalactic.razor:

<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of 
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

DynamicComponent1.razor:

@page "/dynamic-component-1"

<PageTitle>Dynamic Component 1</PageTitle>

<h1>Dynamic Component Example 1</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var entry in components.Keys)
            {
                <option value="@entry">@entry</option>
            }
        </select>
    </label>
</p>

@if (selectedType is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedType" />
    </div>
}

@code {
    private readonly Dictionary<string, Type> components = new()
    {
        ["Rocket Lab"] = typeof(RocketLab),
        ["SpaceX"] = typeof(SpaceX),
        ["ULA"] = typeof(UnitedLaunchAlliance),
        ["Virgin Galactic"] = typeof(VirginGalactic)
    };
    private Type? selectedType;

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && 
            !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedType = components[dropdownValue];
        }
        else
        {
            selectedType = null;
        }
    }
}

RocketLab.razor:

<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

SpaceX.razor:

<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of 
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

UnitedLaunchAlliance.razor:

<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

VirginGalactic.razor:

<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of 
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

DynamicComponentExample1.razor:

@page "/dynamiccomponent-example-1"

<h1><code>DynamicComponent</code> Component Example 1</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var entry in components.Keys)
            {
                <option value="@entry">@entry</option>
            }
        </select>
    </label>
</p>

@if (selectedType is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedType" />
    </div>
}

@code {
    private readonly Dictionary<string, Type> components = new()
    {
        ["Rocket Lab"] = typeof(RocketLab),
        ["SpaceX"] = typeof(SpaceX),
        ["ULA"] = typeof(UnitedLaunchAlliance),
        ["Virgin Galactic"] = typeof(VirginGalactic)
    };
    private Type? selectedType;

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedType = components[dropdownValue];
        }
        else
        {
            selectedType = null;
        }
    }
}

RocketLab.razor:

<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

SpaceX.razor:

<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of 
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

UnitedLaunchAlliance.razor:

<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

VirginGalactic.razor:

<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of 
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

DynamicComponentExample1.razor:

@page "/dynamiccomponent-example-1"

<h1><code>DynamicComponent</code> Component Example 1</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var entry in components.Keys)
            {
                <option value="@entry">@entry</option>
            }
        </select>
    </label>
</p>

@if (selectedType is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedType" />
    </div>
}

@code {
    private readonly Dictionary<string, Type> components = new()
    {
        ["Rocket Lab"] = typeof(RocketLab),
        ["SpaceX"] = typeof(SpaceX),
        ["ULA"] = typeof(UnitedLaunchAlliance),
        ["Virgin Galactic"] = typeof(VirginGalactic)
    };
    private Type? selectedType;

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedType = components[dropdownValue];
        }
        else
        {
            selectedType = null;
        }
    }
}

In the preceding example:

  • An Dictionary<TKey,TValue> is used to manage components to be displayed.
  • Names serve as the dictionary keys and are provided as selection options.
  • Component types are stored as dictionary values using the typeof operator.

Pass parameters

If dynamically-rendered components have component parameters, pass them into the DynamicComponent as an IDictionary<string, object>. The string is the name of the parameter, and the object is the parameter's value.

The following example configures a component metadata object (ComponentMetadata) to supply parameter values to dynamically-rendered components based on the type name. The example is just one of several approaches that you can adopt. Parameter data can also be provided from a web API, a database, or a method. The only requirement is that the approach returns an IDictionary<string, object>.

ComponentMetadata.cs:

namespace BlazorSample;

public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = [];
}
namespace BlazorSample;

public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = [];
}
public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = new();
}
public class ComponentMetadata
{
    public Type? Type { get; init; }
    public string? Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = new();
}

The following RocketLabWithWindowSeat component (RocketLabWithWindowSeat.razor) has been updated from the preceding example to include a component parameter named WindowSeat to specify if the passenger prefers a window seat on their flight:

RocketLabWithWindowSeat.razor:

<h2>Rocket Lab®</h2>

<p>
    User selected a window seat: @WindowSeat
</p>

<p>
    Rocket Lab is a trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    [Parameter]
    public bool WindowSeat { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    User selected a window seat: @WindowSeat
</p>

<p>
    Rocket Lab is a trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    [Parameter]
    public bool WindowSeat { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    User selected a window seat: @WindowSeat
</p>

<p>
    Rocket Lab is a trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    [Parameter]
    public bool WindowSeat { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    User selected a window seat: @WindowSeat
</p>

<p>
    Rocket Lab is a trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    [Parameter]
    public bool WindowSeat { get; set; }
}

In the following example:

  • Only the RocketLabWithWindowSeat component's parameter for a window seat (WindowSeat) receives the value of the Window Seat checkbox.
  • Component names are used as dictionary keys using the nameof operator, which returns component names as constant strings.
  • The dynamically-rendered components are shared components:
    • Shown in this article section: RocketLabWithWindowSeat (RocketLabWithWindowSeat.razor)
    • Components shown in the Example section earlier in this article:
      • SpaceX (SpaceX.razor)
      • UnitedLaunchAlliance (UnitedLaunchAlliance.razor)
      • VirginGalactic (VirginGalactic.razor)

DynamicComponent2.razor:

@page "/dynamic-component-2"

<PageTitle>Dynamic Component 2</PageTitle>

<h1>Dynamic Component Example 2</h1>

<p>
    <label>
        <input type="checkbox" @bind="windowSeat" 
            @bind:after="HandleWindowSeatChanged" />
        Window Seat (Rocket Lab only)
    </label>
</p>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

@code {
    private Dictionary<string, ComponentMetadata> components =
        new()
        {
            [nameof(RocketLabWithWindowSeat)] = new ComponentMetadata()
            {
                Type = typeof(RocketLabWithWindowSeat),
                Name = "Rocket Lab with Window Seat",
                Parameters = { [nameof(RocketLabWithWindowSeat.WindowSeat)] = false }
            },
            [nameof(VirginGalactic)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic),
                Name = "Virgin Galactic"
            },
            [nameof(UnitedLaunchAlliance)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance),
                Name = "ULA"
            },
            [nameof(SpaceX)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX),
                Name = "SpaceX"
            }
        };
    private ComponentMetadata? selectedComponent;
    private bool windowSeat;

    private void HandleWindowSeatChanged()
    {
        components[nameof(RocketLabWithWindowSeat)]
            .Parameters[nameof(RocketLabWithWindowSeat.WindowSeat)] = windowSeat;
    }

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }
}

DynamicComponent2.razor:

@page "/dynamic-component-2"

<PageTitle>Dynamic Component 2</PageTitle>

<h1>Dynamic Component Example 2</h1>

<p>
    <label>
        <input type="checkbox" @bind="windowSeat" 
            @bind:after="HandleWindowSeatChanged" />
        Window Seat (Rocket Lab only)
    </label>
</p>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

@code {
    private Dictionary<string, ComponentMetadata> components =
        new()
        {
            [nameof(RocketLabWithWindowSeat)] = new ComponentMetadata()
            {
                Type = typeof(RocketLabWithWindowSeat),
                Name = "Rocket Lab with Window Seat",
                Parameters = { [nameof(RocketLabWithWindowSeat.WindowSeat)] = false }
            },
            [nameof(VirginGalactic)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic),
                Name = "Virgin Galactic"
            },
            [nameof(UnitedLaunchAlliance)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance),
                Name = "ULA"
            },
            [nameof(SpaceX)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX),
                Name = "SpaceX"
            }
        };
    private ComponentMetadata? selectedComponent;
    private bool windowSeat;

    private void HandleWindowSeatChanged()
    {
        components[nameof(RocketLabWithWindowSeat)]
            .Parameters[nameof(RocketLabWithWindowSeat.WindowSeat)] = windowSeat;
    }

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }
}

DynamicComponentExample2.razor:

@page "/dynamiccomponent-example-2"

<h1><code>DynamicComponent</code> Component Example 2</h1>

<p>
    <label>
        <input type="checkbox" @bind="windowSeat" @bind:after="HandleWindowSeatChanged" />
        Window Seat (Rocket Lab only)
    </label>
</p>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

@code {
    private Dictionary<string, ComponentMetadata> components =
        new()
        {
            [nameof(RocketLabWithWindowSeat)] = new ComponentMetadata()
            {
                Type = typeof(RocketLabWithWindowSeat),
                Name = "Rocket Lab with Window Seat",
                Parameters = { [nameof(RocketLabWithWindowSeat.WindowSeat)] = false }
            },
            [nameof(VirginGalactic)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic),
                Name = "Virgin Galactic"
            },
            [nameof(UnitedLaunchAlliance)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance),
                Name = "ULA"
            },
            [nameof(SpaceX)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX),
                Name = "SpaceX"
            }
        };
    private ComponentMetadata? selectedComponent;
    private bool windowSeat;

    private void HandleWindowSeatChanged()
    {
        components[nameof(RocketLabWithWindowSeat)]
            .Parameters[nameof(RocketLabWithWindowSeat.WindowSeat)] = windowSeat;
    }

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }
}

DynamicComponentExample2.razor:

@page "/dynamiccomponent-example-2"

<h1><code>DynamicComponent</code> Component Example 2</h1>

<p>
    <label>
        <input type="checkbox" @bind="WindowSeat" />
        Window Seat (Rocket Lab only)
    </label>
</p>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

@code {
    private Dictionary<string, ComponentMetadata> components =
        new()
        {
            [nameof(RocketLabWithWindowSeat)] = new ComponentMetadata()
            {
                Type = typeof(RocketLabWithWindowSeat),
                Name = "Rocket Lab with Window Seat",
                Parameters = { [nameof(RocketLabWithWindowSeat.WindowSeat)] = false }
            },
            [nameof(VirginGalactic)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic),
                Name = "Virgin Galactic"
            },
            [nameof(UnitedLaunchAlliance)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance),
                Name = "ULA"
            },
            [nameof(SpaceX)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX),
                Name = "SpaceX"
            }
        };
    private ComponentMetadata? selectedComponent;
    private bool windowSeat;

    private bool WindowSeat
    {
        get { return windowSeat; }
        set
        {
            windowSeat = value;
            components[nameof(RocketLabWithWindowSeat)]
                .Parameters[nameof(RocketLabWithWindowSeat.WindowSeat)] = windowSeat;
        }
    }

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }
}

Event callbacks (EventCallback)

Event callbacks (EventCallback) can be passed to a DynamicComponent in its parameter dictionary.

ComponentMetadata.cs:

namespace BlazorSample;

public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = [];
}
namespace BlazorSample;

public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = [];
}
public class ComponentMetadata
{
    public required Type Type { get; init; }
    public required string Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = new();
}
public class ComponentMetadata
{
    public Type? Type { get; init; }
    public string? Name { get; init; }
    public Dictionary<string, object> Parameters { get; } = new();
}

Implement an event callback parameter (EventCallback) within each dynamically-rendered component.

RocketLab2.razor:

<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

SpaceX2.razor:

<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>SpaceX®</h2>

<p>
    SpaceX is a registered trademark of
    <a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

UnitedLaunchAlliance2.razor:

<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>United Launch Alliance®</h2>

<p>
    United Launch Alliance and ULA are registered trademarks of
    <a href="https://www.ulalaunch.com/">United Launch Alliance, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

VirginGalactic2.razor:

<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}
<h2>Virgin Galactic®</h2>

<p>
    Virgin Galactic is a registered trademark of
    <a href="https://www.virgingalactic.com/">Galactic Enterprises, LLC</a>.
</p>

<button @onclick="OnClickCallback">
    Trigger a Parent component method
</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClickCallback { get; set; }
}

In the following parent component example, the ShowDTMessage method assigns a string with the current time to message, and the value of message is rendered.

The parent component passes the callback method, ShowDTMessage in the parameter dictionary:

  • The string key is the callback method's name, OnClickCallback.
  • The object value is created by EventCallbackFactory.Create for the parent callback method, ShowDTMessage. Note that the this keyword isn't supported in C# field initialization, so a C# property is used for the parameter dictionary.

DynamicComponent3.razor:

@page "/dynamic-component-3"

<PageTitle>Dynamic Component 3</PageTitle>

<h1>Dynamic Component Example 3</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in Components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

<p>
    @message
</p>

@code {
    private ComponentMetadata? selectedComponent;
    private string? message;

    private Dictionary<string, ComponentMetadata> Components =>
        new()
        {
            [nameof(RocketLab2)] = new ComponentMetadata()
            {
                Type = typeof(RocketLab2),
                Name = "Rocket Lab",
                Parameters = { [nameof(RocketLab2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(VirginGalactic2)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic2),
                Name = "Virgin Galactic",
                Parameters = { [nameof(VirginGalactic2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(UnitedLaunchAlliance2)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance2),
                Name = "ULA",
                Parameters = { [nameof(UnitedLaunchAlliance2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(SpaceX2)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX2),
                Name = "SpaceX",
                Parameters = { [nameof(SpaceX2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            }
        };

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = Components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }

    private void ShowDTMessage(MouseEventArgs e) =>
        message = $"The current DT is: {DateTime.Now}.";
}

DynamicComponent3.razor:

@page "/dynamic-component-3"

<PageTitle>Dynamic Component 3</PageTitle>

<h1>Dynamic Component Example 3</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in Components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

<p>
    @message
</p>

@code {
    private ComponentMetadata? selectedComponent;
    private string? message;

    private Dictionary<string, ComponentMetadata> Components =>
        new()
        {
            [nameof(RocketLab2)] = new ComponentMetadata()
            {
                Type = typeof(RocketLab2),
                Name = "Rocket Lab",
                Parameters = { [nameof(RocketLab2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(VirginGalactic2)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic2),
                Name = "Virgin Galactic",
                Parameters = { [nameof(VirginGalactic2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(UnitedLaunchAlliance2)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance2),
                Name = "ULA",
                Parameters = { [nameof(UnitedLaunchAlliance2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(SpaceX2)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX2),
                Name = "SpaceX",
                Parameters = { [nameof(SpaceX2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            }
        };

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = Components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }

    private void ShowDTMessage(MouseEventArgs e) =>
        message = $"The current DT is: {DateTime.Now}.";
}

DynamicComponentExample3.razor:

@page "/dynamiccomponent-example-3"

<h1><code>DynamicComponent</code> Component Example 3</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in Components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

<p>
    @message
</p>

@code {
    private ComponentMetadata? selectedComponent;
    private string? message;

    private Dictionary<string, ComponentMetadata> Components =>
        new()
        {
            [nameof(RocketLab2)] = new ComponentMetadata()
            {
                Type = typeof(RocketLab2),
                Name = "Rocket Lab",
                Parameters = { [nameof(RocketLab2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(VirginGalactic2)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic2),
                Name = "Virgin Galactic",
                Parameters = { [nameof(VirginGalactic2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(UnitedLaunchAlliance2)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance2),
                Name = "ULA",
                Parameters = { [nameof(UnitedLaunchAlliance2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(SpaceX2)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX2),
                Name = "SpaceX",
                Parameters = { [nameof(SpaceX2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            }
        };

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = Components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }

    private void ShowDTMessage(MouseEventArgs e) =>
        message = $"The current DT is: {DateTime.Now}.";
}

DynamicComponentExample3.razor:

@page "/dynamiccomponent-example-3"

<h1><code>DynamicComponent</code> Component Example 3</h1>

<p>
    <label>
        Select your transport:
        <select @onchange="OnDropdownChange">
            <option value="">Select a value</option>
            @foreach (var c in Components)
            {
                <option value="@c.Key">@c.Value.Name</option>
            }
        </select>
    </label>
</p>

@if (selectedComponent is not null)
{
    <div class="border border-primary my-1 p-1">
        <DynamicComponent Type="selectedComponent.Type"
            Parameters="selectedComponent.Parameters" />
    </div>
}

<p>
    @message
</p>

@code {
    private ComponentMetadata? selectedComponent;
    private string? message;

    private Dictionary<string, ComponentMetadata> Components =>
        new()
        {
            [nameof(RocketLab2)] = new ComponentMetadata()
            {
                Type = typeof(RocketLab2),
                Name = "Rocket Lab",
                Parameters = { [nameof(RocketLab2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(VirginGalactic2)] = new ComponentMetadata()
            {
                Type = typeof(VirginGalactic2),
                Name = "Virgin Galactic",
                Parameters = { [nameof(VirginGalactic2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(UnitedLaunchAlliance2)] = new ComponentMetadata()
            {
                Type = typeof(UnitedLaunchAlliance2),
                Name = "ULA",
                Parameters = { [nameof(UnitedLaunchAlliance2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            },
            [nameof(SpaceX2)] = new ComponentMetadata()
            {
                Type = typeof(SpaceX2),
                Name = "SpaceX",
                Parameters = { [nameof(SpaceX2.OnClickCallback)] =
                    EventCallback.Factory.Create<MouseEventArgs>(this, ShowDTMessage) }
            }
        };

    private void OnDropdownChange(ChangeEventArgs e)
    {
        if ((e.Value is string dropdownValue) && !String.IsNullOrWhiteSpace(dropdownValue))
        {
            selectedComponent = Components[dropdownValue];
        }
        else
        {
            selectedComponent = null;
        }
    }

    private void ShowDTMessage(MouseEventArgs e) =>
        message = $"The current DT is: {DateTime.Now}.";
}

Avoid catch-all parameters

Avoid the use of catch-all parameters. If catch-all parameters are used, every explicit parameter on DynamicComponent effectively is a reserved word that you can't pass to a dynamic child. Any new parameters passed to DynamicComponent are a breaking change, as they start shadowing child component parameters that happen to have the same name. It's unlikely that the caller always knows a fixed set of parameter names to pass to all possible dynamic children.

Access the dynamically-created component instance

Use the Instance property to access the dynamically-created component instance.

Create an interface to describe the dynamically-created component instance with any methods and properties that you need to access from the parent component when the component is dynamically loaded. The following example specifies a Log method for implementation in components.

Interfaces/ILoggable.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.Interfaces;

public interface ILoggable : IComponent
{
    public void Log();
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Interfaces;

public interface ILoggable : IComponent
{
    public void Log();
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Interfaces;

public interface ILoggable : IComponent
{
    public void Log();
}
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Interfaces;

public interface ILoggable : IComponent
{
    public void Log();
}

Each component definition implements the interface. The following example is a modified Rocket Lab® component from the Example section that logs a string via its Log method.

RocketLab3.razor:

@using BlazorSample.Interfaces
@implements ILoggable
@inject ILogger<RocketLab3> Logger
    
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    public void Log() => Logger.LogInformation("Woot! I logged this call!");
}
@using BlazorSample.Interfaces
@implements ILoggable
@inject ILogger<RocketLab3> Logger
    
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    public void Log() => Logger.LogInformation("Woot! I logged this call!");
}
@using BlazorSample.Interfaces
@implements ILoggable
@inject ILogger<RocketLab3> Logger
    
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    public void Log()
    {
        Logger.LogInformation("Woot! I logged this call!");
    }
}
@using BlazorSample.Interfaces
@implements ILoggable
@inject ILogger<RocketLab3> Logger
    
<h2>Rocket Lab®</h2>

<p>
    Rocket Lab is a registered trademark of 
    <a href="https://www.rocketlabusa.com/">Rocket Lab USA Inc.</a>
</p>

@code {
    public void Log()
    {
        Logger.LogInformation("Woot! I logged this call!");
    }
}

The remaining three shared components (VirginGalactic3, UnitedLaunchAlliance3, SpaceX3) receive similar treatment:

  • The following directives are added to the components, where the {COMPONENT TYPE} placeholder is the component type:

    @using BlazorSample.Interfaces
    @implements ILoggable
    @inject ILogger<{COMPONENT TYPE}> Logger
    
  • Each component implements a Log method. The log category written by the logger includes the fully-qualified name of the component type when LogInformation is called:

    @code {
        public void Log()
        {
            Logger.LogInformation("Woot! I logged this call!");
        }
    }
    

The parent component casts the dynamically-loaded component instance as an ILoggable to access members of the interface. In the following example, the loaded component's Log method is called when a button is selected in the UI:

...
@using BlazorSample.Interfaces

...

<DynamicComponent Type="..." @ref="dc" />

...

<button @onclick="LogFromLoadedComponent">Log from loaded component</button>

@code {
    private DynamicComponent? dc;

    ...

    private void LogFromLoadedComponent() => (dc?.Instance as ILoggable)?.Log();
}

For a working demonstration of the preceding example, see the DynamicComponent4 component in the Blazor sample app.

For a working demonstration of the preceding example, see the DynamicComponent4 component in the Blazor sample app.

For a working demonstration of the preceding example, see the DynamicComponentExample4 component in the Blazor sample app.

For a working demonstration of the preceding example, see the DynamicComponentExample4 component in the Blazor sample app.

Trademarks

Rocket Lab is a registered trademark of Rocket Lab USA Inc. SpaceX is a registered trademark of Space Exploration Technologies Corp. United Launch Alliance and ULA are registered trademarks of United Launch Alliance, LLC. Virgin Galactic is a registered trademark of Galactic Enterprises, LLC.

Additional resources