Blazor页面元素除了Body之外

8
我有一个Blazor服务器端应用程序,试图理解其结构。在MainLayout.razor页面中,我看到@Body标记,这是每个页面内容呈现的位置。
我想知道是否可以向mainLayout页面添加额外的呈现元素?例如@Header部分。而且我更喜欢在每个单独的页面内定义此部分。
换句话说,对于每个页面,除了主要内容之外,还需要定义标题、页脚或我在MainLayout中定义的任何其他呈现元素。这样,我就可以拥有唯一适用于每个页面的自定义的标题/页脚元素。
感谢您的帮助。

我不知道您的布局页面是否能够检测到导航事件:https://blazor-university.com/routing/detecting-navigation-events/,并更改布局以满足您的要求。 - Eliseo
2个回答

12

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

在任何页面上,单击鼠标右键以查看网站源代码。您也可以使用开发人员工具(通常是通过按F12键打开)来检查特定元素的HTML和CSS。

@page "/"

<h1>Hello, world!</h1>

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

1
看起来不错。它完全是无空值吗? - H H
@HenkHolterman https://github.com/BrianLParker/LayoutDemo - Brian Parker
非常感谢,Brian。我会尝试一下的。 - Franky
我在这个库中做了类似的事情,当时我还没有足够聪明地使用渲染片段 :) https://www.nuget.org/packages/CleanView/ - Brian Parker
出色的概念验证,可以创建更复杂的场景。谢谢。 - Aaron Hudon
显示剩余4条评论

3

这里是一个更加动态的解决方案:

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

enter image description here

这也清除了导航上的值,因为我正在使用IDisposable。Setter不必设置(Null是可以的)。如果使用多个Setter,则最新的Setter优先。我尚未测试在一个页面上动态删除多个Setter。
这里有一个repo,使用WebAssembly 3.2.1。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接