如何用rendertreebuilder动态构建父子组件?

How to dynamically build parent and child components with rendertreebuilder?

我需要有关使用 rendertreebuilder 动态 building/generating 多步组件的帮助。

我有两个组件:MultistepComponent(父级)和 MultiStepNavigation(子级)。我还有一个组件构建器 class 来构建这些组件。

渲染组件不工作,我得到一个错误:

Object reference not set to an instance of an object.

那是因为MultiStepNavigation中的MultiStepComponent属性是空的。在 razor 页面中使用组件不会出现此错误。

我将发布大量代码,但您不必阅读每个细节。我只想提供所有信息。

MultiStepComponent.razor

<CascadingValue Value="this">
    <div id="@Id">
        <ul class="nav nav-pills nav-justified">
            @foreach (var step in Steps)
            {
                <li id="step-@(StepsIndex(step) + 1)" class="nav-item">
                    <a class="nav-link @((ActiveStep == step) ? "active" : "")" href="javascript: void(0)"
                       @onclick="@(e=> SetActive(step))">@step.Name</a>
                </li>
            }
        </ul>
        <div id="container-fluid">
            <div class="navigatingBtns">
                <button class="btn btn-primary btn-lg" type="button"
                        disabled="@(ActiveStepIndex == 0)" @onclick="GoBack">
                    Previous
                </button>
                <button class="btn btn-primary btn-lg"
                        type="@(IsLastStep ? "submit" : "button")" @onclick="GoNext">
                    @(IsLastStep ? "Submit" : "Next")
                </button>
            </div>
            @ChildContent
        </div>
        </div>
</CascadingValue>

MultiStepComponent.razor.cs

public partial class MultiStepComponent { 

        protected internal List<MultiStepNavigation> Steps = new List<MultiStepNavigation>();

        [Parameter]
        public string Id { get; set; }

        [Parameter]
        public RenderFragment? ChildContent { get; set; }

        [Parameter]
        public MultiStepNavigation? ActiveStep { get; set; }

        [Parameter]
        public int ActiveStepIndex { get; set; }

        public bool IsLastStep { get; set; }

        protected internal void GoBack()
        {
            if (ActiveStepIndex > 0)
            {
                SetActive(Steps[ActiveStepIndex - 1]);
            }
        }

        protected internal void GoNext()
        {
            if (ActiveStepIndex < Steps.Count - 1)
            {
                SetActive(Steps[(Steps.IndexOf(ActiveStep) + 1)]);
            }
        }

        protected internal void SetActive(MultiStepNavigation step)
        {
            ActiveStepIndex = StepsIndex(step);
            if (ActiveStepIndex == Steps.Count - 1)
            {
                IsLastStep = true;
            }
            else
            {
                IsLastStep = false;
            }
        }

        public int StepsIndex(MultiStepNavigation step) => StepsIndexInternal(step);
        protected int StepsIndexInternal(MultiStepNavigation step)
        {
            return Steps.IndexOf(step);
        }

        protected internal void AddStep(MultiStepNavigation step)
        {
            Steps.Add(step);
        }

        protected override void OnAfterRender(bool firstRender)
        {
            if (firstRender)
            {
                SetActive(Steps[0]);
                StateHasChanged();
            }
        }
    }

MultiStepNavigation.razor

@if (MultiStepComponent.ActiveStep == this) { 
    <div id="step-@(MultiStepComponent.StepsIndex(this) + 1)">
        @ChildContent
    </div>
}

MultiStepNavigation.razor.cs

public partial class MultiStepNavigation
    {
        [CascadingParameter]
        protected internal MultiStepComponent MultiStepComponent { get; set; }

        [Parameter]
        public RenderFragment ChildContent { get; set; } = default!;

        [Parameter]
        public string Name { get; set; } = "";

        protected override void OnInitialized()
        {
            if(MultiStepComponent != null)
            {
                MultiStepComponent.AddStep(this);
            }
        }
    }

构建组件的代码:

renderFragment = b =>
            {
                b.OpenComponent<MultiStepComponent>(0);
                b.AddAttribute(1, "id", "MultiStepContainer");
                b.OpenComponent<MultiStepNavigation>(2);
                b.AddAttribute(3, "Name", "First Step");
                b.CloseComponent();
                b.CloseComponent();
            };

自己在razor page上做组件不报错。它看起来像这样:

<MultiStepComponent Id="MultiStepContainer">
                <MultiStepNavigation Name="First Step"></MultiStepNavigation>    
            </MultiStepComponent>

我做错了什么??

您的代码没有考虑到 MultiStepNavigationMultiStepComponent 的子内容这一事实。

查看 /obj/debug/net5.0/razor/...,找到您的组件的 razor 版本并查看代码。

您可能需要这样做:

private RenderFragment _MultiStepNavigationFragment => b =>
{
        b.OpenComponent<MultiStepNavigation>(0);
        b.AddAttribute(1, "Name", "First Step");
        b.AddAttribute(2, "ChildContent", this.ChildContent);
        b.CloseComponent();
};
private RenderFragment _MultiStepComponentFragment => b =>
{
        b.OpenComponent<MultiStepComponent>(0);
        b.AddAttribute(1, "id", "MultiStepContainer");
        b.AddAttribute(2, "ChildContent", this._MultiStepNavigationFragment);
        b.CloseComponent();
};