我需要动态地将Blazor组件放入用户提供的内容中。本质上,该组件应该使用一些UI元素扩展用户提供的标记。
比方说,用户提供了一些可以在其中包含“ greeting-container”元素的内容,并且该组件应在该元素内插入一个问候按钮。
我当前的解决方案是调用JavaScript函数来移动OnAfterRenderAsync
中的DOM元素(下面的完整代码)。它似乎工作正常,但是在Blazor中似乎不建议使用DOM元素,因为它会影响差异算法。所以我对此有两个问题:
RenderTreeBuilder
,但似乎并非为此目的而设计,因为建议使用硬编码的序列号,这在处理编译时未知的动态内容时似乎不可能当前解决方案代码:
Greeter.razor
@page "/greeter"
@inject IJSRuntime JSRuntime;
<div>
@((MarkupString)UserContentMarkup)
<div id="greeting">
<button @onclick="ToggleGreeting">Toggle greeting</button>
@if (isGreetingVisible) {
<p>Hello, @Name!</p>
}
</div>
</div>
@code {
[Parameter]
public string UserContentMarkup { get; set; }
[Parameter]
public string Name { get; set; }
private bool isGreetingVisible;
private void ToggleGreeting()
{
isGreetingVisible = !isGreetingVisible;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JSRuntime.InvokeVoidAsync("moveGreetingToContainer");
}
}
_ Host.cshtml
window.moveGreetingToContainer = () => {
var greeting = document.getElementById("greeting");
var container = document.getElementById("greeting-container");
container.appendChild(greeting);
}
UserContentTest.razor
@page "/userContentTest"
@inject IJSRuntime JSRuntime;
<h3>Testing user content</h3>
<Greeter UserContentMarkup=@userContentMarkup Name="John"></Greeter>
@code {
private string userContentMarkup = "Some <b>HTML</b> text followed by greeting <div id='greeting-container'></div> and <i>more</i> text";
}
预期结果(点击“切换问候”后):
<div>
Some <b>HTML</b> text followed by greeting
<div id="greeting-container">
<div id="greeting">
<button>Toggle greeting</button>
<p>Hello, John!</p>
</div>
</div> and <i>more</i> text
</div>
答案 0 :(得分:1)
一个好问题-是的,由于Blazor看不到您对dom所做的更改,因此使用JS移动dom元素非常糟糕。
您可以做的是切换到使用RenderFragment
,更具体地说是RenderFragment<RenderFragment>
,它是将提供更多标记作为参数的标记。
在第二行中,我正在调用UserContentMarkup方法(它是RenderFragment<RenderFragment>
),并将<div id=greeting>
内容作为context
参数传递。
注意:它包装在<text>
元素中,这实际上是将HTML嵌入Razor文件中的C#中的一种方法。它不会在页面上呈现<text>
元素。
<div>
@UserContentMarkup(
@<text>
<div id="greeting">
<button @onclick="ToggleGreeting">Toggle greeting</button>
@if (isGreetingVisible) {
<p>Hello, @Name!</p>
}
</div>
</text>
)
</div>
@code {
[Parameter]
public RenderFragment<RenderFragment> UserContentMarkup { get; set; }
[Parameter]
public string Name { get; set; }
private bool isGreetingVisible;
private void ToggleGreeting()
{
isGreetingVisible = !isGreetingVisible;
}
}
在这里,您可以看到两种使用Greeter的方法-使用页面中的标记或使用code
方法。
<h3>Testing user content</h3>
@* Using markup to supply user content - @context is where the button goes *@
<Greeter Name="John">
<UserContentMarkup>
Some <b>HTML</b> text followed by greeting
<div id='greeting-container'>@context</div> and <i>more</i> text
</UserContentMarkup>
</Greeter>
@* Using a method to supply the user content - @context is where the button goes *@
<Greeter Name="John" UserContentMarkup=@userContent />
此code
方法可能令人困惑-它是RenderFragment<RenderFragment>
,这意味着它必须是一个接受RenderFragment
作为其唯一参数并返回{{1} }-在这种情况下返回的RenderFragment
被标记包裹在RenderFragment
中,以明确表示它是标记。
<text>