具有自定义子组件的自定义 Blazor 组件

Posted

技术标签:

【中文标题】具有自定义子组件的自定义 Blazor 组件【英文标题】:Custom Blazor component with custom child components 【发布时间】:2020-11-16 02:12:02 【问题描述】:

我最近开始使用 Blazor,发现它是一项非常有前途的技术。

我正在制作自定义的嵌套 Blazor 组件,但我似乎没有让它按我想要的方式工作。

目标是拥有一个“Content”组件,该组件具有“ContentHeader”和“ContentBody”属性。但是,“ContentHeader”应该是另一个具有“Title”和“ActionBar”属性的自定义组件。所需的用法如下:

<Content>
    <ContentHeader>        
        <Title>
            Title
        </Title>        
        <ActionBar>
            <button>Test button</button>
        </ActionBar>
    </ContentHeader>
    <ContentBody>
        Body
    </ContentBody>
</Content>

“内容”组件:

<CascadingValue Value="this">
    <div class="content-container">
        @ContentHeader(Header)
        <div class="content-body">
            @ContentBody
        </div>
    </div>
</CascadingValue>

@code 
    [Parameter]
    public RenderFragment<ContentHeader> ContentHeader  get; set; 

    [Parameter]
    public RenderFragment ContentBody  get; set; 

    public ContentHeader Header  get; set; 

    public async Task SetContentHeader(ContentHeader contentHeader)
    
        Header = contentHeader;
        await InvokeAsync(StateHasChanged);
    

“ContentHeader”组件:

<div class="content-header">
    <h3>@Title</h3>
    <div class="content-header-action-bar">
        @ActionBar
    </div>
</div>

@code 
    [CascadingParameter]
    public Content Content  get; set; 

    [Parameter]
    public RenderFragment Title  get; set; 

    [Parameter]
    public RenderFragment ActionBar  get; set; 

    protected override async Task OnInitializedAsync()
    
        await Content.SetContentHeader(this);
        await base.OnInitializedAsync();
    

我得到的实际渲染输出是这样的:

<div class="content-container">
    <!--!-->
        Title
    <!--!-->        
    <!--!-->
    <button>Test button</button>
    <!--!-->
    <div class="content-body">
        <!--!-->
        Body
    </div>
</div>

似乎“ContentHeader”组件的标记被完全忽略了。 “HeaderComponent”标题周围的&lt;h3&gt;&lt;/h3&gt;标签丢失,并且按钮未放置在&lt;div class="content-header-action-bar"&gt;&lt;/div&gt;

我还注意到“ContentHeader”的OnInitializedAsync 没有被触发。

我做错了什么?我是否使用了正确的方法?

非常感谢。

亲切的问候

【问题讨论】:

【参考方案1】:

您尝试这样做的方式不正确,除非您将 @ActionBar 呈现为您自己,我认为您没有尝试这样做。

这是来自我的开源项目 Blazor Chat:

来源: https://github.com/DataJuggler/BlazorChat

实时站点:一次没有多少用户 https://blazorchat.com

这是我的索引页面。 ScreenType 是一个枚举变量。

@if (ScreenType == ScreenTypeEnum.Join)
 
    <Join Parent=this></Join>

else if (ScreenType == ScreenTypeEnum.Login)

    <Login Parent=this></Login>

else

    <Chat Parent=this></Chat>
    @if (TextHelper.Exists(Message))
      
        <div class="message">
            @Message
        </div>
    
  

通过Parent=this,子组件与父组件和页面可以通过接口进行通信。我不打算在这里讨论,但如果有人好奇,这里是我大约六个月前写的关于这个主题的博客文章:

https://datajugglerblazor.blogspot.com/2020/01/how-to-use-interfaces-to-communicate.html

我不能发布整个项目,但这里有一些相关部分,可能会帮助您更好地了解父/子组件。

这是一个组件项目中的例子,有多个子组件:

聊天剃须刀:

@if (HasUser)

    @if (Connected)
    
        <div class="chatroom2">
            @if (ListHelper.HasOneOrMoreItems(Messages))
            
                foreach (SubscriberMessage message in Messages)
                
                    <SpeechBubble Message=message>
                    </SpeechBubble>
                
            
        </div>
        <div class="whoseonlist">            
            @if (ListHelper.HasOneOrMoreItems(Names))
              
                @foreach (SubscriberCallback callback in Names)
                
                    <button class="listbutton" @onclick="(Action<EventArgs>) (args => 
SendPrivateMessageClicked(args, callback.Id))">@callback.Name</button><br />
                
            
            <textarea class="typedtext" @bind="MessageText"></textarea>
            <button class="greenbutton8" 
@onclick="BroadCastMessage">Broadcast</button>
            <div class="privatemessageinfo">
                Click a name to send<br />a private message.
            </div>
        </div> 
    
    else
     
        <div id="ChatRoom" class="chatroom">
            <div class="moveup4">
                Enter Chat<br />
                <input type="text" @bind="SubscriberName" /><br />
                <button class="greenbutton6" 
@onclick="@RegisterWithServer">Connect</button>
            </div>
        </div>    
    

else

    <div class="loginorjointochat">
    </div>

如果您确实想要呈现 HTML,语音气泡有一个 message 属性,它会清理 HTML。因此,如果用户输入,我发现这个很酷的网站https://pixeldatabase.net(我的网站),经过消毒的代码呈现为超链接(我进行文本替换并将其转换为锚标记),但如果消息包含粗体标签,文本呈现为粗体,或者 HTML 换行符用于分行等。

foreach (SubscriberMessage message in Messages)

    <SpeechBubble Message=message>
    </SpeechBubble>

这要归功于这个 Nuget 包: HtmlSanitizer

它的参考是 Ganss.XSS,我花了一段时间才弄明白。

如果有人想要,这里有一个带有扩展方法的类:

using Ganss.XSS;
using Microsoft.AspNetCore.Components;

namespace BlazorChat

    public static class MarkupStringExtensions
    
        public static MarkupString Sanitize(this MarkupString markupString)
        
            return new MarkupString(SanitizeInput(markupString.Value));
        

        private static string SanitizeInput(string value)
        
            var sanitizer = new HtmlSanitizer();
            return sanitizer.Sanitize(value);
        
    

这是呈现的 HTML 文本的屏幕截图:

【讨论】:

我可以看到模板化组件的使用,但您到底在哪里使用自定义嵌套组件? 正如我在帖子中解释的那样,Chat 组件显示了一个消息列表,每条消息都是一个 SpeechBubble 组件。

以上是关于具有自定义子组件的自定义 Blazor 组件的主要内容,如果未能解决你的问题,请参考以下文章

XamarinAndroid组件教程设置自定义子元素动画

Blazor 内置组件上的自定义 CSS

Blazor 中隐藏/非绑定字段的自定义验证

微信小程序自定义事件

Blazor University (10)组件 — 捕获意外参数

具有三个组件的自定义 UIPickerView,每个组件在选择指示器上显示标签