Элементы страницы Блейзора, отличные от тела

#blazor #blazor-server-side

#блейзор #блейзор- на стороне сервера

Вопрос:

У меня есть серверное приложение Blazor, которое пытается понять структуру. Внутри страницы mainLayout.razor я вижу тег @Body, и именно здесь отображается содержимое каждой страницы.

Мне интересно, можно ли добавить дополнительный элемент рендеринга на страницу основного описания? Например, раздел @Header. И я бы предпочел также определить этот раздел внутри каждой отдельной страницы.

Другими словами, для каждой страницы, помимо основного содержимого, также необходимо определить верхний, нижний колонтитул или любой другой элемент рендеринга, который я определяю в mainLayout. Таким образом, я могу настроить элементы верхнего и нижнего колонтитулов, которые уникальны для каждой страницы.

Спасибо за любую помощь.

Комментарии:

1. Я не знаю, можете ли вы на своей странице макета обнаружить события навигации: blazor-university.com/routing/detecting-navigation-events и измените макет в соответствии с вашими требованиями

Ответ №1:

MainLayout.razor

Примечание: использование метода для обновления полей фрагмента рендеринга, которые я намеренно сделал закрытыми, которые затем вызываются StateHasChanged() . Другие методы могут быть легко созданы для очистки или установки других полей.

 @inherits LayoutComponentBase

<CascadingValue Value="this">
    <div class="page">
        <div class="sidebar">
            <NavMenu />
        </div>
        <div class="main">
            <div class="top-row px-4">
                @header
            </div>

            <div class="content px-4">
                @Body
                @footer
            </div>
        </div>
    </div>
</CascadingValue>

@code {
    private RenderFragment header;
    private RenderFragment footer;

    public void SetHeaderAndFooter(RenderFragment header, RenderFragment footer)
    {
        this.header = header;
        this.footer = footer;
        StateHasChanged();
    }
}

  

LayoutSetter.cs

 
public class LayoutSetter : ComponentBase
{
    [CascadingParameter]
    public MainLayout Layout { get; set; }

    [Parameter]
    public RenderFragment Header { get; set; }

    [Parameter]
    public RenderFragment Footer { get; set; }

    protected override void OnInitialized()
    {
        Layout.SetHeaderAndFooter(Header, Footer);
    }
}

  

На любой странице…

 @page "/"

<h1>Hello, world!</h1>

<LayoutSetter>
    <Header>
        Hello
    </Header>
    <Footer>
        Goodbye
    </Footer>
</LayoutSetter>
  

Комментарии:

1. Выглядит красиво. Это полностью нулевое доказательство?

2. @ХенкХолтерман github.com/BrianLParker/LayoutDemo

3. Большое спасибо, Брайан, я попробую.

4. Я сделал что-то подобное в этой библиотеке, тогда я был недостаточно умен, чтобы использовать фрагменты рендеринга 🙂 nuget.org/packages/CleanView

5. Я внедрил ваш код, Брайан, работает как шарм, еще раз спасибо.

Ответ №2:

Вот более динамичное решение:

MainLayout.razor

 @inherits LayoutComponentBase

<div class="sidebar">
    <AppNavigation />
</div>

<div class="main">
    <div class="top-row px-4">
        <AppHeader />
    </div>

    <div class="content px-4">
        @Body
    </div>

    <div class="bottom-row px-4">
        <AppFooter />
    </div>
</div>

  

AppHeader.razor

 @implements IDisposable

@LayoutService.Header
@code {
    [Inject]
    public ILayoutService LayoutService { get; set; }

    protected override void OnInitialized()
    {
        LayoutService.PropertyChanged  = LayoutService_PropertyChanged;
        base.OnInitialized();
    }

    private void LayoutService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(ILayoutService.Header))
        {
            StateHasChanged();
        }
    }

    public void Dispose()
    {
        if (LayoutService != null)
        {
            LayoutService.PropertyChanged -= LayoutService_PropertyChanged;
        }
    }

}
  

ILayoutService.cs

 public interface ILayoutService
{    
    RenderFragment Header { get; }    
    SetHeader HeaderSetter { get; set; }
    event PropertyChangedEventHandler PropertyChanged;    
    void UpdateHeader();
}
  

LayoutService.cs

 public class LayoutService : ILayoutService, INotifyPropertyChanged
{

    public RenderFragment Header => HeaderSetter?.ChildContent;

    public SetHeader HeaderSetter
    {
        get => headerSetter;
        set
        {
            if (headerSetter == value) return;
            headerSetter = value;
            UpdateHeader();
        }
    }

    public void UpdateHeader() => NotifyPropertyChanged(nameof(Header));

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    private SetHeader headerSetter;
}

  

Установщик

SetHeader.cs

 public class SetHeader : ComponentBase, IDisposable
{
    [Inject]
    private ILayoutService Layout { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    protected override void OnInitialized()
    {
        if (Layout != null)
        {
            Layout.HeaderSetter = this;
        }
        base.OnInitialized();
    }

    protected override bool ShouldRender()
    {
        var shouldRender = base.ShouldRender();
        if (shouldRender)
        {
            Layout.UpdateHeader();
        }
        return shouldRender;
    }

    public void Dispose()
    {
        if (Layout != null)
        {
            Layout.HeaderSetter = null;
        }
    }

}
  

Предоставить услугу:

В моем случае Program.cs

  builder.Services.AddScoped<ILayoutService, LayoutService>();
  

Пример

 @page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

<SetHeader>
    <p>Hello Current count: @currentCount</p>
</SetHeader>

<SetFooter>
    <p>Goodbye Current count: @currentCount</p>
</SetFooter>

@code {
    private int currentCount = 0;
    private void IncrementCount() => currentCount  ;
}

  

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

Это также очищает значения при навигации, которые я использую IDisposable . Установщик не обязательно должен быть установлен (значение Null в порядке). Если используется более одного параметра настройки, последний имеет приоритет. Я не тестировал динамическое удаление нескольких сеттеров на одной странице.

Вот репозиторий с использованием WebAssembly 3.2.1