【问题标题】:Attach blazor component dynamically动态附加 Blazor 组件
【发布时间】:2020-02-25 10:01:41
【问题描述】:

我可以在 C# 中创建 Blazor 组件的实例并在之后附加它吗? 或者,可以从 C# 代码中动态地在 DOM 中创建一个组件并取回对它的引用吗?

我正在创建一个由 C# 代码触发的“弹出对话框”。

Dialog.razor

<div>
    Dialog Text: @Text
</div>

@code{
    public string Text { get; set; }
}

尝试失败

<div>
    @foreach (var d in List)
    {
        <div>@d</div><!--this doesn't work since d is not a RenderFragment-->
    }
</div>

@code{
    public List<Dialog> List { get; set; } = new List<Dialog>();

    void AddDialog()
    {
        var d = new Dialog();
        d.Text = "Hello " + List.Count;
        List.Add(d);
    }
}

这里的问题是我还没有找到将 ComponentBase 的实例附加到 DOM 上的方法。 有没有办法获取 RenderFragment?

第二个问题是在附加对话框之前子组件不会初始化,因此首先限制了可以做的事情。

【问题讨论】:

    标签: blazor


    【解决方案1】:

    这是另一种方法......

    AlertMessage.razor

    <div style="border: 1px solid red; width: 500px; height:auto; margin: 3px; 
      padding:0px;">
        <div style="height:auto; width:inherit; padding:5px; border: 1px solid 
        blue; text-align:right;">
            <span style="float:left">@Title</span>
            <a href="#" @onclick="@(() => Close.InvokeAsync(ID))" 
       role="button">X</a>
            <div><input type="text" value="@content" /></div>
        </div>
         <div style="padding:25px; ">@ChildContent</div> 
    
     </div>
    
    @code {
      [Parameter]
      public int ID { get; set; }
      [Parameter]
      public string Title { get; set; }
      [Parameter]
      public RenderFragment ChildContent { get; set; }
      [Parameter]
      public EventCallback <int> Close {get; set;}
    
    }
    

    AlertMessageGroup.razor

    @using Microsoft.AspNetCore.Components.CompilerServices;
    
    
    <h3>AlertMessageGroup</h3>
    @if (alerts.Count > 0)
    {
    <p>Contains @alerts.Count AlertMessage Components</p>
    
    @foreach (var alert in alerts)
     {
           <p>Alert ID: @alert.ID</p>
    
     }
    }
    
    <div>
    
        @foreach (var alert in alerts)
        {
            @RenderAlert(alert);
    
        }
    
    
    </div>
    
    
    
    @code {
    
    List<Alert> alerts = new List<Alert>
            {
                new Alert{ ID = 1, Title = "First Message", Message = "This is my 
                                                                first message" },
                new Alert{ ID = 2, Title = "Second Message", Message = "This is 
                                                            my second message" },
                new Alert{ ID = 3, Title = "Third Message", Message = "This is my 
                                                                 third message" }
            };
    
    
    private RenderFragment RenderAlert(Alert alert) => builder =>
    {
    
        builder.OpenComponent(0, typeof(AlertMessage));
        builder.AddAttribute(1, "ID", alert.ID);
        builder.AddAttribute(2, "Title", alert.Title);
    
    
        builder.AddAttribute(3, "ChildContent", (RenderFragment)((builder) =>
        {
            builder.AddContent(4, alert.Message);
    
    
        }
        ));
    
         builder.AddAttribute(5, "Close", EventCallback.Factory.Create<int>
                                              (this, RemoveAlertMessage));
         builder.CloseComponent();
    
      };
    
    
    
    public void RemoveAlertMessage(int ID)
    {
    
        alerts.Remove( alerts.Where(alert => alert.ID == ID).FirstOrDefault());
        StateHasChanged();
    }
    
    
    public class Alert
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Message { get; set; }
    
    }
    
    }
    

    用法

    @page "/"
    
    <AlertMessageGroup />
    

    【讨论】:

    • 我是否更正了存储在列表中的实例与呈现的实例不同。这意味着如果我实现一个调用 StateHasChanged() 的方法 SetMessage(),它将不会在渲染的组件上调用,而是在列表中的实例上调用。
    • 我没有创建要渲染的组件列表。我创建了一个 Alert 对象列表,将参数传递给渲染组件的 RenderAlert 方法。当我想删除(或添加)一个组件时,我从 Alert 对象列表中删除(或添加)一个 Alert 对象,然后调用 StateHasChanged 方法,该方法告诉父组件其状态已更改,并且应该重新渲染。
    • "它不会在渲染的组件上调用,而是在列表中的实例上调用。" StateHasChanged 方法是一个相当简单的方法,其作用是通知组件其状态已更改。 。 就这些。是否重新渲染取决于组件。如果您存储组件列表并更改这些组件的值,则实际上不会发生任何事情,除非您调用 StateHasChanged 方法来通知组件其状态已更改,从而导致重新渲染(如果适用)。
    • 您存储在列表中的组件当然包含您分配给它们的新值,并且重新渲染反映了这一点。
    • 我明白了,每次渲染都会使用更新的属性重新创建。
    【解决方案2】:

    这是一种方法

    1. 创建一个Placeholder 对象并将其放入列表中
    2. 剃刀页面将从列表中创建一个新实例。
    3. 捕获实例引用并将其传递回调用代码
    4. 等待OnAfterRender,然后再对实例进行操作。

    Dialog.razor
    不知道它是动态加载的。

    <div>
        Dialog Text: @Text
        <Red @ref="Red"></Red>
    </div>
    
    @code{
        public Red Red { get; set; }
    
        public string Text { get; set; }
    
        public void SetText(string text)
        {
            this.Text = text;
            StateHasChanged();
        }
    }
    

    DynamicWrapper.razor 用于通知组件何时完全加载

    @ChildContent
    
    @code{
        [Parameter]
        public RenderFragment ChildContent { get; set; }
    
        [Parameter]
        public EventCallback AfterFirstRender { get; set; }
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
                await AfterFirstRender.InvokeAsync(null);
    
            base.OnAfterRender(firstRender);
        }
    }
    

    TestPage.razor

    <div><button @onclick="AddDialog">Add one more</button></div>
    
    <div>
        @foreach (var p in List)
        {
            <DynamicWrapper AfterFirstRender="p.AfterFirstRender">
                <Dialog @ref="p.Dialog"></Dialog>
            </DynamicWrapper>
        }
    </div>
    
    @code{
        public class Placeholder<T>
        {
            public T Dialog { get; set; }
    
            TaskCompletionSource<T> task = new TaskCompletionSource<T>();
    
            public void AfterFirstRender(object args)
            {
                task.SetResult(Dialog);
            }
    
            public Task<T> GetDialog() => task.Task;
        }
    
        public List<Placeholder<Dialog>> List { get; set; } = new List<Placeholder<Dialog>>();
    
        async Task AddDialog()
        {
            var p = new Placeholder<Dialog>();
            List.Add(p);
            var d = await p.GetDialog();
            d.SetText("Hello");
            d.Red.Show();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-04
      • 2021-03-03
      • 2018-10-22
      • 2021-09-05
      • 2018-04-17
      • 2018-10-14
      • 2018-02-13
      • 1970-01-01
      • 2022-01-01
      相关资源
      最近更新 更多