37

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

我找不到任何方法来做到这一点。我对这个链接上的一些动态内容进行了一些实验,但没有得到任何结果。

4

4 回答 4

41

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

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

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

简单的条件渲染

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

@code {
  bool _showCounter = true;
  int _count;
}

简单的重复数据集

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

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

  @foreach (var salesOrderLine in _salesOrder.salesOrderlines)
  {
    <SalesOrderLine SOLine=@salesOrderLine />
  };

如果您有一个添加另一个销售订单行的按钮,那么您只需_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>

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() };
}

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

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

结论

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

尽管 MVC 的 razor 页面和 Blazor 的语法相似,但 Blazor 模型在概念上更接近 React 之类的东西,而不是 MVC,了解背景中有一些类似于 shadow-DOM 的东西非常重要.

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

于 2019-05-08T17:52:39.223 回答
28

对于 0.2 版,这是 Steve Sanderson 的回答:

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

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

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

在这里找到

于 2018-05-01T18:17:05.203 回答
1

当然,您也可以使用BuildRenderTree但通常不推荐这样做,因为它可能会搞砸。(微软文档

public class CustomLabel: ComponentBase
{  
    [Parameter] public string Id { get; set; }
    [Parameter] public string? Label { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        if (!string.IsNullOrEmpty(Label))
        {
            builder.OpenElement(1, "label");
            builder.AddAttribute(2, "for", Id);
            builder.AddAttribute(3, "class", "custom-css-classes-here");
            builder.AddContent(4, Label);
            builder.CloseElement();
        }
    }

    // another example: maybe you would have an "IsRequired" flag 
    // that would add an "*" after the label if set to true
}
于 2021-07-26T14:37:23.930 回答
0

<DynamicComponent>使这一切变得相对微不足道。它是 .NET 6 的核心新增功能之一,将于 2021 年 11 月正式发布。

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

这是我发现的两个最好的实现参考;

于 2021-10-26T01:05:37.973 回答