【问题标题】:Blazor Inheritance with ChildContent RenderFragments?Blazor 继承与 ChildContent RenderFragments?
【发布时间】:2021-01-29 17:50:46
【问题描述】:

假设我们有一个基本组件ParentComponent.razor

<div id="ParentComponent">@ChildContent</div>

@code {
    [Parameter] public RenderFragment ChildContent {get;set;}
}

可以这样使用:

<ParentComponent>
    <div>content</div>
</ParentComponent>

会渲染:

<div id="ParentComponent"><div>content</div></div>

但是假设我们希望 ChildComponent.razor 继承自 ParentComponent.razor

@inherits ParentComponent

<div id="ChildComponent">@ChildContent</div>

@code {
    [Parameter] public RenderFragment ChildContent {get;set;}
}

有没有办法从 ChildComponent 填充 ParentComponent 的 @ChildContentRenderFragment?

<ChildComponent>
    child content
</ChildComponent>

制作这个:

<div id="ParentComponent">
    <div id="ChildComponent">child content</div>
</div>

不知道我是否遗漏了一些明显的东西,但一直在弄乱它并且无法在互联网上找到任何答案。

编辑 - 替代解决方案

我发现了另一种方法,它不依赖于直接覆盖 BuildRenderTree 方法,并且允许您使用 html/razor 标记,尽管这确实意味着在房屋周围走动。也不确定这是否安全或高效,但为了完整起见将其放在这里。

ParentElement.razor

@using Microsoft.AspNetCore.Components.Rendering;

@if (!HasRendered()) {
    SetRenderedStatus();
    <div id="ParentComponent">
        RenderChild(__builder);
    </div>
}

@code {
    protected bool _hasBaseRendered = false;

    protected bool HasRendered() {
        return _hasBaseRendered;
    }

    protected void SetRenderedStatus() {
         _hasBaseRendered = true;
    }

    protected virtual void RenderChild(RenderTreeBuilder __builder) { }
}

ChildElement.razor

@using Microsoft.AspNetCore.Components.Rendering
@inherits ParentElement

@if (HasRendered()) {
    <div id="ChildComponent">
         @ChildContent
    </div>
}
@{
    base.BuildRenderTree(__builder);
}

@code {
    [Parameter] public RenderFragment ChildContent { get; set; }

    protected override void RenderChild(RenderTreeBuilder __builder) {
        this.BuildRenderTree(__builder);
    }
}

如此快速的解释,IDE/Compiler 中的功能清楚地将代码的 razor/html 标记部分编译为幕后的 BuildRenderTree 方法,这就是为什么您不能在 .razor 中覆盖这些方法的原因组件。

上面的代码编译成:

ChildElement.razor

protected override void BuildRenderTree(RenderTreeBuilder __builder)
{
    if (HasRendered())
    {
        __builder.AddContent(0, "    ");
        __builder.OpenElement(1, "div");
        __builder.AddAttribute(2, "id", "ChildComponent");
        __builder.AddMarkupContent(3, "\r\n        ");
        __builder.AddContent(4, ChildContent);
        __builder.AddMarkupContent(5, "\r\n    ");
        __builder.CloseElement();
        __builder.AddMarkupContent(6, "\r\n");          
    }
    base.BuildRenderTree(__builder);
}

所以当 HasRendered 为 false 时 ChildContent 被忽略,它被定向调用父类上的 base.BuildRenderTree

ParentElement.razor

protected override void BuildRenderTree(RenderTreeBuilder __builder)
{
    if (!HasRendered())
    {
        SetRenderedStatus();
        __builder.AddContent(0, "        ");
        __builder.OpenElement(1, "div");
        __builder.AddAttribute(2, "id", "ParentComponent");
        __builder.AddMarkupContent(3, "\r\n");
        RenderChild(__builder);
        __builder.AddContent(4, "        ");
        __builder.CloseElement();
        __builder.AddMarkupContent(5, "\r\n");
    }
}

它呈现父母标记,中途被定向到RenderChild,它被孩子覆盖,这将流回到孩子的一个级别,然后调用它的BuildRenderTreeHasRendered检查将确保避免无限循环,然后呈现标记的子部分,然后返回到父级以完成。

这可能会被重构,只是我当前的工作副本实验。

【问题讨论】:

    标签: blazor blazor-server-side


    【解决方案1】:

    复制并测试...

    父组件.cs

    public partial class ParentComponent : ComponentBase
    {
        [Parameter] public RenderFragment ChildContent { get; set; }
    
        protected override void BuildRenderTree(RenderTreeBuilder builder)
        {
            builder.OpenElement(0, "div");
            builder.AddAttribute(1, "id", "ParentComponent");
            builder.AddContent(2, ChildContent);
            builder.CloseElement();
            
            base.BuildRenderTree(builder);
        }
    }
    

    子组件.cs

    public partial class ChildComponent : ParentComponent
    {
        protected override void BuildRenderTree(RenderTreeBuilder builder)
        {
            builder.OpenComponent<ParentComponent>(0);
            builder.AddAttribute(1, "ChildContent", (RenderFragment)((builder2) => 
           {
                builder2.OpenElement(1, "div");
                builder2.AddAttribute(2, "id", "ChildComponent");
                builder2.AddContent(3, ChildContent);
                builder2.CloseElement();
            } ));
    
            builder.CloseComponent();
        
        }
    }
    

    Index.razor

    @page "/"
    
    <ChildComponent>child content</ChildComponent>
    

    【讨论】:

      【解决方案2】:

      我刚刚在 5.0 rc2 上测试了这个。

      @inherits ParentComponent
      
      <div id="ChildComponent">@ParentRenderFragment</div>
      
      @code {
      
          RenderFragment ParentRenderFragment => builder =>
          {
              builder.OpenComponent<ParentComponent>(1);
              builder.AddAttribute(2, "ChildContent", this.ChildContent);
              // Additional inherited parameter's here
              builder.CloseComponent();
          };
      
      }
      

      制作了这个:

      注意:

      ID 不正确,因为它们是作为渲染片段的一部分传递的。如果您无权编辑子组件,这可能需要 dom 操作来修复,因为您需要捕获属性或将 id 作为参数传递。

      然而,这显示了如何将父母原始渲染到孩子中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-10
        • 2021-02-12
        • 1970-01-01
        • 2020-06-17
        • 2012-10-30
        • 1970-01-01
        • 1970-01-01
        • 2020-05-16
        相关资源
        最近更新 更多