Blazor - 如何动态创建组件

Die*_*rdk 3 c# asp.net-core blazor

我想测试是否可以动态创建Blazor组件.

我找不到任何办法这样做.我已经尝试了一些在此链接上找到的动态内容,但最终没有任何结果.

tom*_*dox 36

从对已接受答案的评论和对这个答案的原始版本的评论来看,我认为动态添加组件可能会有些混乱。有(至少)几种方法可以实现这一点(以及一些关于此的现有问题,例如here)。这完全取决于您所说的“动态”是什么意思:

1) 在 Razor 代码中使用条件语句

如果您想要实现的只是根据数据或模型中的某种状态显示或隐藏组件,那么动态呈现组件的“正常”方法是在 Razor 视图中使用某种条件代码。

简单的条件渲染

@if (_showCounter)
{
  <MyCounterComponent Count="@_count" />
}

@code {
  bool _showCounter = true;
  int _count;
}
Run Code Online (Sandbox Code Playgroud)

简单重复数据集

对于重复的数据集,例如项目列表,您可以利用 Blazor 的数据绑定。

以显示销售订单的页面/组件为例,然后每个销售订单行都有一个子组件。您的 razor 页面上可能有如下所示的代码:

  @foreach (var salesOrderLine in _salesOrder.salesOrderlines)
  {
    <SalesOrderLine SOLine=@salesOrderLine />
  };
Run Code Online (Sandbox Code Playgroud)

如果您有一个添加另一个销售订单行的按钮,那么您可以简单地将新记录添加到该_salesOrder按钮单击事件中的模型/视图模型。按钮点击通常会触发重新渲染,因此页面应该会自动显示一个额外的SalesOrderLine组件(如果没有,您可以使用this.StateHasChanged();它来告诉它事情不同并导致重新渲染)

包含不同数据类型(可能是多态的)的重复数据集

如果您的列表包含不同的类型,您可以使用 switch 语句来决定要呈现哪种类型的组件,例如(来自这个 GitHub 问题):

<ul>
    @foreach (fruit in fruits)
    {
        switch(fruit)
        {
            case PearComponent p:
                <PearComponent ParameterOfSomeSort="p"></PearComponent>
                <li>Or render pears like this, if you want the li in it</li>
                break;
            case AppleComponent a:
                <AppleComponent></AppleComponent>
                break;
            case BananaComponent b:
                <BananaComponent></BananaComponent>
                break;
            case RaspberryComponent r:
                <RaspberryComponent></RaspberryComponent>
                break;
        }
    }
</ul>
Run Code Online (Sandbox Code Playgroud)

2. 使用RenderFragment动态渲染

使用上面的 Razor 方法无法很好地处理某些情况。在这些情况下,RenderFragment提供了另一种动态呈现页面部分的方法。

多态列表

如果您有一个真正的多态列表(例如,所有实现相同接口或从同一类继承的对象列表),那么可以从这个 GitHub 帖子中使用这种类型的方法:

@page "/"

@foreach (CounterParent counter in components)
{
    RenderFragment renderFragment = (builder) => { builder.OpenComponent(0, counter.GetType()); builder.CloseComponent(); };
    <div>
        <div>Before the component</div>
        @renderFragment
        <div>Afterthe component</div>
    </div>
}

@code
{
    List<CounterParent> components = new List<CounterParent>() { new CounterParent(), new CounterChild() };
}
Run Code Online (Sandbox Code Playgroud)

Blazor 团队正在考虑改进Blazor中处理多态列表的方式

mkArtakMSFT 于 2019 年 10 月 1 日发表评论感谢您与我们联系,@Joebeazelman。使用您拥有的切换方法将是我们的建议。未来我们会考虑在这方面做得更好。

结论

这里的关键点(对于那些来自 MVC 背景的人)是没有必要像在 MVC 中那样尝试手动将新 HTML 注入到 DOM 中,或者动态加载部分视图,Blazor 会这样做你。

尽管 MVC 的 razor 页面和 Blazor 的 razor 页面之间的语法相似,但 Blazor 模型在概念上更接近于 React 之类的东西,而不是 MVC,理解在后台有一些类似 shadow-DOM 的东西真的很重要.

此页面对 Blazor 中的数据绑定有一些很好的指导。

  • 什么?!动态渲染组件非常有用并且非常常见。例如,您将如何处理组件的多态列表,其中每个成员都实现自己独特的视觉呈现,而这些视觉呈现在编译时无法确定,因为它们可能是外部插件组件?在这种情况下,你的方法完全失败了。 (13认同)

Flo*_*res 9

对于0.2版本,这是史蒂夫桑德森的答案:

我们将来会实现更好的API来构建RenderFragments,但是现在你可以

@CreateDynamicComponent();
@functions {
    RenderFragment CreateDynamicComponent() => builder =>
    {
        builder.OpenComponent(0, typeof(SurveyPrompt));
        builder.AddAttribute(1, "Title", "Some title");
        builder.CloseComponent();
    };
}
Run Code Online (Sandbox Code Playgroud)

这些是非常低级的API(甚至没有记录),所以我们希望现在没有多少人需要这样做.更高级别的API将在稍后出现.

这里找到

  • “我们希望没有多少人需要这样做” - 任何设计器应用程序都必须具有动态添加组件的能力。所见即所得的编辑器、流程建模 UI、仪表板 UI...所以这并不是罕见的、外星的需求... (10认同)
  • 也许它是低级别的,但它有文档[RenderTreeBuilder.OpenComponent方法](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.rendering.rendertreebuilder.opencomponent?view=aspnetcore -3.1#Microsoft_AspNetCore_Components_Rendering_RenderTreeBuilder_OpenComponent__1_System_Int32_) (4认同)

Mem*_*can 7

<DynamicComponent>使得这一切变得相对简单。它是 .NET 6 的核心新增内容之一,将于 2021 年 11 月正式发布。

我现在正在 RC2 上探索它,发现它使用起来非常简单,尽管有关不同使用模式的文档很少。

以下是我发现的两个最佳实施参考;