动态呈现的 ASP.NET Core Razor 组件
作者:Dave Brock
使用内置 DynamicComponent 组件按类型呈现组件。
DynamicComponent 适用于呈现组件,而无需循环访问可能的类型或使用条件逻辑。 例如,DynamicComponent 可以基于用户从下拉列表中选择的内容来呈现组件。
如下示例中:
componentType
指定类型。parameters
指定要传递给componentType
组件的组件参数。
<DynamicComponent Type="@componentType" Parameters="@parameters" />
@code {
private Type componentType = ...;
private IDictionary<string, object> parameters = ...;
}
有关传递参数值的详细信息,请参阅本文后面的传递参数部分。
使用 Instance 属性访问动态创建的组件实例:
<DynamicComponent Type="@typeof({COMPONENT})" @ref="dc" />
<button @onclick="Refresh">Refresh</button>
@code {
private DynamicComponent? dc;
private Task Refresh()
{
return (dc?.Instance as IRefreshable)?.Refresh();
}
}
在上面的示例中:
{COMPONENT}
占位符是动态创建的组件类型。IRefreshable
是开发人员为动态组件实例提供的示例接口。
示例
在下面的示例中,Razor 组件基于用户在四个可能值的下拉列表中选择的内容来呈现组件。
用户 spaceflight 运营商选择 | 共享 Razor 组件以呈现 |
---|---|
Rocket Lab® | Shared/RocketLab.razor |
SpaceX® | Shared/SpaceX.razor |
ULA® | Shared/UnitedLaunchAlliance.razor |
Virgin Galactic® | Shared/VirginGalactic.razor |
Shared/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>
Shared/SpaceX.razor
:
<h2>SpaceX®</h2>
<p>
SpaceX is a registered trademark of
<a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>
Shared/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>
Shared/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>
Pages/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>
<option value="@nameof(RocketLab)">Rocket Lab</option>
<option value="@nameof(SpaceX)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance)">ULA</option>
<option value="@nameof(VirginGalactic)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType" />
</div>
}
@code {
private Type? selectedType;
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
Shared/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>
Shared/SpaceX.razor
:
<h2>SpaceX®</h2>
<p>
SpaceX is a registered trademark of
<a href="https://www.spacex.com/">Space Exploration Technologies Corp.</a>
</p>
Shared/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>
Shared/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>
Pages/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>
<option value="@nameof(RocketLab)">Rocket Lab</option>
<option value="@nameof(SpaceX)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance)">ULA</option>
<option value="@nameof(VirginGalactic)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType" />
</div>
}
@code {
private Type? selectedType;
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
在上面的示例中:
- 使用
nameof
运算符将组件名称作为常量字符串返回,将组件名称用作选项值。 - 应用的命名空间为
BlazorSample
。 更改命名空间以与应用的命名空间匹配。
传递参数
如果动态呈现的组件具有组件参数,请将它们作为 IDictionary<string, object>
传入 DynamicComponent。 string
是参数的名称,object
是参数的值。
下面的示例配置组件元数据对象 (ComponentMetadata
),以根据类型名称向动态呈现的组件提供参数值。 该示例只是可以采用的几种方法之一。 还可以从 web API、数据库或方法提供参数数据。 唯一的要求是方法返回 IDictionary<string, object>
。
ComponentMetadata.cs
:
public class ComponentMetadata
{
public string? Name { get; set; }
public Dictionary<string, object> Parameters { get; set; } =
new Dictionary<string, object>();
}
以下 RocketLabWithWindowSeat
组件 (Shared/RocketLabWithWindowSeat.razor
) 已对前面的示例进行了更新,其中包含名为 WindowSeat
的组件参数,用于指定乘客是否更喜欢航班上的靠窗座位:
Shared/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; }
}
在以下示例中:
- 只有靠窗座位 (
WindowSeat
) 的RocketLabWithWindowSeat
组件参数会接收Window Seat
复选框的值。 - 应用的命名空间为
BlazorSample
。 更改命名空间以与应用的命名空间匹配。 - 动态呈现的组件是应用
Shared
文件夹中的共享组件:- 本部分内容:
RocketLabWithWindowSeat
(Shared/RocketLabWithWindowSeat.razor
) - 本文前面的示例部分所示的组件:
SpaceX
(Shared/SpaceX.razor
)UnitedLaunchAlliance
(Shared/UnitedLaunchAlliance.razor
)VirginGalactic
(Shared/VirginGalactic.razor
)
- 本部分内容:
Pages/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 (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@components[selectedType.Name].Parameters" />
</div>
}
@code {
private Dictionary<string, ComponentMetadata> components =
new()
{
{
"RocketLabWithWindowSeat",
new ComponentMetadata
{
Name = "Rocket Lab with Window Seat",
Parameters = new() { { "WindowSeat", false } }
}
},
{
"VirginGalactic",
new ComponentMetadata { Name = "Virgin Galactic" }
},
{
"UnitedLaunchAlliance",
new ComponentMetadata { Name = "ULA" }
},
{
"SpaceX",
new ComponentMetadata { Name = "SpaceX" }
}
};
private Type? selectedType;
private bool windowSeat;
private bool WindowSeat
{
get { return windowSeat; }
set
{
windowSeat = value;
components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] =
windowSeat;
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
@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 (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@components[selectedType.Name].Parameters" />
</div>
}
@code {
private Dictionary<string, ComponentMetadata> components =
new()
{
{
"RocketLabWithWindowSeat",
new ComponentMetadata
{
Name = "Rocket Lab with Window Seat",
Parameters = new() { { "WindowSeat", false } }
}
},
{
"VirginGalactic",
new ComponentMetadata { Name = "Virgin Galactic" }
},
{
"UnitedLaunchAlliance",
new ComponentMetadata { Name = "ULA" }
},
{
"SpaceX",
new ComponentMetadata { Name = "SpaceX" }
}
};
private Type? selectedType;
private bool windowSeat;
private bool WindowSeat
{
get { return windowSeat; }
set
{
windowSeat = value;
components[nameof(RocketLabWithWindowSeat)].Parameters["WindowSeat"] =
windowSeat;
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
}
事件回调 (EventCallback
)
事件回调 (EventCallback) 可以传递给参数字典中的 DynamicComponent。
ComponentMetadata.cs
:
public class ComponentMetadata
{
public string? Name { get; set; }
public Dictionary<string, object> Parameters { get; set; } =
new Dictionary<string, object>();
}
在每个动态呈现的组件中实现事件回调参数 (EventCallback)。
Shared/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; }
}
Shared/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; }
}
Shared/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; }
}
Shared/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; }
}
在以下父组件示例中,ShowDTMessage
方法将一个包含当前时间的字符串分配给 message
,并呈现 message
的值。
父组件在参数字典中传递回调方法 ShowDTMessage
:
string
键是回调方法的名称OnClickCallback
。object
值由 EventCallbackFactory.Create 为父回调方法ShowDTMessage
创建。 请注意,C# 字段中不支持this
关键字,因此 C# 属性用于参数字典。
对于以下组件,请更改 OnDropdownChange
方法中 BlazorSample
的命名空间名称,以匹配应用的命名空间。
Pages/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>
<option value="@nameof(RocketLab2)">Rocket Lab</option>
<option value="@nameof(SpaceX2)">SpaceX</option>
<option value="@nameof(UnitedLaunchAlliance2)">ULA</option>
<option value="@nameof(VirginGalactic2)">Virgin Galactic</option>
</select>
</label>
</p>
@if (selectedType is not null)
{
<div class="border border-primary my-1 p-1">
<DynamicComponent Type="@selectedType"
Parameters="@Components[selectedType.Name].Parameters" />
</div>
}
<p>
@message
</p>
@code {
private Type? selectedType;
private string? message;
private Dictionary<string, ComponentMetadata> Components
{
get
{
return new Dictionary<string, ComponentMetadata>()
{
{
"RocketLab2",
new ComponentMetadata
{
Name = "Rocket Lab",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"VirginGalactic2",
new ComponentMetadata
{
Name = "Virgin Galactic",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"UnitedLaunchAlliance2",
new ComponentMetadata
{
Name = "ULA",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
},
{
"SpaceX2",
new ComponentMetadata
{
Name = "SpaceX",
Parameters =
new()
{
{
"OnClickCallback",
EventCallback.Factory.Create<MouseEventArgs>(
this, ShowDTMessage)
}
}
}
}
};
}
}
private void OnDropdownChange(ChangeEventArgs e)
{
selectedType = e.Value?.ToString()?.Length > 0 ?
Type.GetType($"BlazorSample.Shared.{e.Value}") : null;
}
private void ShowDTMessage(MouseEventArgs e) =>
message = $"The current DT is: {DateTime.Now}.";
}
避免 catch-all 参数
避免使用 catch-all 参数。 如果使用了 catch-all 参数,则 DynamicComponent 上的每个显式参数都会成为无法传递给动态子级的保留字。 传递给 DynamicComponent 的任何新参数都是中断性变更,因为它们会开始隐藏恰好同名的子组件参数。 调用方不太可能始终了解要传递给所有可能的动态子级的一组固定的参数名称。
商标
Rocket Lab 是 Rocket Lab USA Inc. 的注册商标。SpaceX 是 Space Exploration Technologies Corp. 的注册商标。United Launch Alliance 和 ULA 是 United Launch Alliance, LLC 的注册商标。 Virgin Galactic 是 Galactic Enterprises, LLC 的注册商标。