Andere Blazor-Seitenelemente als Body
Ich habe eine serverseitige Blazor-App, die versucht, die Struktur zu verstehen. Auf der MainLayout.razor-Seite wird das Tag @Body angezeigt. Hier wird der Inhalt jeder Seite gerendert.
Ich frage mich, ob es möglich ist, der mainLayout-Seite ein zusätzliches Renderelement hinzuzufügen. Zum Beispiel ein @ Header-Abschnitt. Und ich würde es vorziehen, diesen Abschnitt auch auf jeder einzelnen Seite zu definieren.
Mit anderen Worten, für jede Seite muss neben dem Hauptinhalt auch die Kopf- und Fußzeile oder ein anderes Renderelement definiert werden, das ich im MainLayout definiert habe. Auf diese Weise kann ich Kopf- / Fußzeilenelemente anpassen, die für jede Seite eindeutig sind.
Vielen Dank für jede Hilfe.
Antworten
MainLayout.razor
Hinweis: Die Verwendung einer Methode zum Aktualisieren der Renderfragmentfelder, die ich absichtlich privat gemacht habe und die dann aufruft StateHasChanged(). Andere Methoden könnten leicht erstellt werden, um andere Felder zu löschen oder festzulegen.
@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);
}
}
Auf jeder Seite ...
@page "/"
<h1>Hello, world!</h1>
<LayoutSetter>
<Header>
Hello
</Header>
<Footer>
Goodbye
</Footer>
</LayoutSetter>
Hier ist eine dynamischere Lösung:
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;
}
Der Setter
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;
}
}
}
Stellen Sie den Service bereit:
In meinem Fall Program.cs
builder.Services.AddScoped<ILayoutService, LayoutService>();
Beispiel
@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++;
}
Dadurch werden auch die Werte in der Navigation gelöscht, die ich verwende IDisposable. Der Setter muss nicht gesetzt werden (Null ist ok). Wenn mehr als ein Setter verwendet wird, hat der neueste Vorrang. Ich habe nicht getestet, mehrere Setter auf einer Seite dynamisch zu entfernen.
Hier ist ein Repo mit WebAssembly 3.2.1