Attach blazor component dynamically
我可以在 C# 中创建 Blazor 组件的实例并在之后附加它吗?
或者,是否可以从 C# 代码在 DOM 中动态创建一个组件并取回对它的引用?
我正在创建一个由 C# 代码触发的"弹出对话框"。
Dialog.razor
1 2 3 4 5 6 | Dialog Text: @Text @code{ public string Text { get; set; } } |
尝试失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @foreach (var d in List) { @d<!--this doesn't work since d is not a RenderFragment--> } @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?
第二个问题是,在附加对话框之前子组件不会初始化,因此首先限制了可以做的事情。
这是另一种方法……
AlertMessage.razor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <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> Close.InvokeAsync(ID))" role="button">X <input type="text" value="@content" /> @ChildContent @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | @using Microsoft.AspNetCore.Components.CompilerServices; AlertMessageGroup @if (alerts.Count > 0) { <p>Contains @alerts.Count AlertMessage Components</p> @foreach (var alert in alerts) { <p>Alert ID: @alert.ID</p> } } @foreach (var alert in alerts) { @RenderAlert(alert); } @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; } } } |
用法
1 2 3 | @page"/" <AlertMessageGroup /> |
这是一种方法
Dialog.razor
不知道它是动态加载的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Dialog Text: @Text <Red @ref="Red"></Red> @code{ public Red Red { get; set; } public string Text { get; set; } public void SetText(string text) { this.Text = text; StateHasChanged(); } } |
DynamicWrapper.razor
用于通知组件何时完全加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <button @onclick="AddDialog">Add one more</button> @foreach (var p in List) { <DynamicWrapper AfterFirstRender="p.AfterFirstRender"> <Dialog @ref="p.Dialog"></Dialog> </DynamicWrapper> } @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(); } } |