在用户内容中定位Blazor组件



我需要在用户提供的内容中动态放置Blazor组件。从本质上讲,该组件应该使用一些UI元素来扩展用户提供的标记。

假设用户提供一些内容;问候容器";元素,组件应该在该元素中插入一个问候按钮。

我目前的解决方案是调用一个JavaScript函数来移动OnAfterRenderAsync中的DOM元素(完整代码如下(。它似乎工作得很好,但在Blazor中似乎不鼓励操作DOM元素,因为它会影响困难的算法。所以我有几个问题:

  1. 像这样移动DOM元素有多糟糕?它是否会导致性能问题、功能问题或某些未定义的行为
  2. 有没有更好的方法可以在不使用JavaScript的情况下实现相同的结果?我曾考虑使用RenderTreeBuilder,但它似乎不是为此目的而设计的,因为建议使用硬编码序列号,这在处理编译时未知的动态内容时似乎是不可能的

当前解决方案代码:

贪婪剃刀

@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.rarzor

@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>

好问题-是的,使用JS移动dom元素非常糟糕,因为Blazor没有看到您对dom所做的更改。

您可以做的是切换到使用RenderFragment,更具体地说,RenderFragment<RenderFragment>是一种标记,它将提供更多标记作为参数。

在第二行中,我调用UserContentMarkup方法(它是RenderFragment<RenderFragment>(,并传入<div id=greeting>内容作为context参数。

注意:它被封装在<text>元素中,这实际上是将C#中的HTML嵌入Razor文件的一种方式。它不会向页面呈现<text>元素

Greeter.razor

<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;
}
}

UserContentTest.razor

在这里,您可以看到两种使用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作为其唯一参数并返回RenderFragment的方法——在这种情况下返回的RenderFragment是用<text>包装的标记,以表明它是标记。

@code
{
RenderFragment<RenderFragment> userContent 
=> context => @<text>Some stuff @context more stuff</text>;
}

在这里试试:https://blazorrepl.com/repl/QuPPaMEu34yA5KSl40

相关内容

  • 没有找到相关文章

最新更新