Пользовательский компонент Blazor с пользовательскими дочерними компонентами

Недавно я начал использовать 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 его разметка полностью игнорирует. Теги <h3></h3> вокруг заголовка компонента заголовка отсутствуют, и кнопка не помещена внутри <div class="content-header-action-bar"></div>

Я также заметил, что OnInitializedAsync ContentHeader не запускается.

Что я делаю неправильно? Я вообще использую правильный подход?

Спасибо заранее.

С уважением




Ответы (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

Я не могу опубликовать весь проект, но вот несколько важных частей, которые могут помочь вам лучше понять родительские / дочерние компоненты.

Это пример в проекте компонента, имеющего несколько дочерних компонентов:

Chat.razor:

@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, у облачка речи есть свойство сообщения, и оно очищает HTML. Итак, если пользователь вводит, я обнаружил этот классный сайт https://pixeldatabase.net (мой сайт), Sanitized код отображается как гиперссылка (я заменяю текст и превращаю его в тег привязки), но также, если сообщение содержит тег, выделенный жирным шрифтом, текст отображается как полужирный, или разрывы строк 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:

введите описание изображения здесь

person Community    schedule 26.07.2020
comment
Я вижу использование шаблонных компонентов, но где именно вы используете пользовательские вложенные компоненты? - person ChThy; 27.07.2020
comment
Как я объяснил в этой публикации, компонент Chat отображает список сообщений, и каждое сообщение является компонентом SpeechBubble. - person ; 27.07.2020