Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Notitie
Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Waarschuwing
Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.
Belangrijk
Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.
Zie de .NET 9-versie van dit artikelvoor de huidige release.
In dit artikel wordt uitgelegd hoe u gegevens van een bovenliggend Razor-onderdeel kunt overdragen naar onderliggende onderdelen.
Trapsgewijze waarden en parameters bieden een handige manier om gegevens binnen een componenthiërarchie van een bovenliggend onderdeel naar een willekeurig aantal onderliggende onderdelen te laten vloeien. In tegenstelling tot -componentparameterszijn er voor trapsgewijze waarden en parameters geen kenmerktoewijzingen vereist voor elk afstammend component waarin de gegevens worden gebruikt. Met trapsgewijze waarden en parameters kunnen onderdelen ook met elkaar samenwerken in een onderdeelhiërarchie.
Notitie
De codevoorbeelden in dit artikel maken gebruik van nullable reference types (NRT's) en .NET-compiler statische analyse van null-status, die worden ondersteund in ASP.NET Core in .NET 6 of hoger. Bij het targeten van .NET 5 of eerder, verwijder de null-typeaanduiding (?
) uit de CascadingType?
, @ActiveTab?
, RenderFragment?
, ITab?
, TabSet?
, en string?
typen in de voorbeelden van het artikel.
Trapsgewijze waarden op rootniveau
Trapsgewijze waarden op hoofdniveau kunnen worden geregistreerd voor de hele onderdeelhiërarchie. Benoemde trapsgewijze waarden en abonnementen voor updatemeldingen worden ondersteund.
De volgende klasse wordt gebruikt in de voorbeelden van deze sectie.
Dalek.cs
:
// "Dalek" ©Terry Nation https://www.imdb.com/name/nm0622334/
// "Doctor Who" ©BBC https://www.bbc.co.uk/programmes/b006q2x0
namespace BlazorSample;
public class Dalek
{
public int Units { get; set; }
}
// "Dalek" ©Terry Nation https://www.imdb.com/name/nm0622334/
// "Doctor Who" ©BBC https://www.bbc.co.uk/programmes/b006q2x0
namespace BlazorSample;
public class Dalek
{
public int Units { get; set; }
}
De volgende registraties worden in het Program
-bestand van de app gemaakt met AddCascadingValue:
-
Dalek
met een eigenschapswaarde voorUnits
wordt geregistreerd als een vaste trapsgewijze waarde. - Een tweede
Dalek
registratie met een andere eigenschapswaarde voorUnits
heet 'AlphaGroup
'.
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
In het volgende Daleks
-onderdeel worden de trapsgewijze waarden weergegeven.
Daleks.razor
:
@page "/daleks"
<PageTitle>Daleks</PageTitle>
<h1>Root-level Cascading Value Example</h1>
<ul>
<li>Dalek Units: @Dalek?.Units</li>
<li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
[CascadingParameter]
public Dalek? Dalek { get; set; }
[CascadingParameter(Name = "AlphaGroup")]
public Dalek? AlphaGroupDalek { get; set; }
}
@page "/daleks"
<PageTitle>Daleks</PageTitle>
<h1>Root-level Cascading Value Example</h1>
<ul>
<li>Dalek Units: @Dalek?.Units</li>
<li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
[CascadingParameter]
public Dalek? Dalek { get; set; }
[CascadingParameter(Name = "AlphaGroup")]
public Dalek? AlphaGroupDalek { get; set; }
}
In het volgende voorbeeld wordt Dalek
geregistreerd als een trapsgewijze waarde met behulp van CascadingValueSource<T>
, waarbij <T>
het type is. De vlag isFixed
geeft aan of de waarde is opgelost. Als false
, zijn alle geadresseerden geabonneerd op updatemeldingen. Abonnementen maken overhead en verminderen de prestaties, dus stel isFixed
in op true
als de waarde niet verandert.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Waarschuwing
Het registreren van een componenttype als een root-level trapsgewijze waarde registreert geen extra services voor het type en staat serviceactivatie in de component niet toe.
Behandel vereiste services afzonderlijk van trapsgewijze waarden en registreer ze afzonderlijk van het trapsgewijs type.
Vermijd het gebruik van AddCascadingValue om een onderdeeltype te registreren als trapsgewijze waarde. In plaats daarvan verpakt u de <Router>...</Router>
in het Routes
-onderdeel (Components/Routes.razor
) met het onderdeel en gaat u over op wereldwijde interactieve server-side rendering (interactieve SSR). Zie de sectie van het
Cascaderende waarden op wortelniveau met meldingen
Door NotifyChangedAsync aan te roepen om updatemeldingen te versturen, kan aan meerdere Razor componentabonnees worden gesignaleerd dat een trapsgewijze waarde is gewijzigd. Meldingen zijn niet mogelijk voor abonnees die statische server-side rendering (statische SSR) gebruiken, dus abonnees moeten een interactieve rendermodus gebruiken.
In het volgende voorbeeld:
-
NotifyingDalek
implementeert om clients op de hoogte te stellen dat een eigenschapswaarde INotifyPropertyChanged is gewijzigd. Wanneer deUnits
eigenschap is ingesteld, wordt de PropertyChangedEventHandler (PropertyChanged
) aangeroepen. - De methode
SetUnitsToOneThousandAsync
kan door abonnees worden geactiveerd omUnits
in te stellen op 1000, met een gesimuleerde vertraging in de verwerking.
Houd er rekening mee dat bij productiecode elke verandering in de toestand (elke verandering in een eigenschapswaarde van de klasse) ervoor zorgt dat alle geabonneerde componenten opnieuw worden herladen, ongeacht welk deel van de toestand ze gebruiken. U wordt aangeraden gedetailleerde klassen te maken en deze afzonderlijk te cascaderen met specifieke abonnementen, om ervoor te zorgen dat alleen onderdelen die geabonneerd zijn op een specifiek deel van de applicatiestatus door wijzigingen worden beïnvloed.
Notitie
Voor een Blazor Web App oplossing die bestaat uit server- en clientprojecten (.Client
) wordt het volgende NotifyingDalek.cs
bestand in het .Client
project geplaatst.
NotifyingDalek.cs
:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class NotifyingDalek : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private int units;
public int Units
{
get => units;
set
{
if (units != value)
{
units = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged(
[CallerMemberName] string? propertyName = default)
=> PropertyChanged?.Invoke(this, new(propertyName));
public async Task SetUnitsToOneThousandAsync()
{
// Simulate a three second delay in processing
await Task.Delay(3000);
Units = 1000;
}
}
CascadingStateServiceCollectionExtensions
Het volgende maakt een CascadingValueSource<TValue> van een type dat INotifyPropertyChanged implementeert.
Notitie
Voor een Blazor Web App oplossing die bestaat uit server- en clientprojecten (.Client
) wordt het volgende CascadingStateServiceCollectionExtensions.cs
bestand in het .Client
project geplaatst.
CascadingStateServiceCollectionExtensions.cs
:
using System.ComponentModel;
using Microsoft.AspNetCore.Components;
namespace Microsoft.Extensions.DependencyInjection;
public static class CascadingStateServiceCollectionExtensions
{
public static IServiceCollection AddNotifyingCascadingValue<T>(
this IServiceCollection services, T state, bool isFixed = false)
where T : INotifyPropertyChanged
{
return services.AddCascadingValue<T>(sp =>
{
return new CascadingStateValueSource<T>(state, isFixed);
});
}
private sealed class CascadingStateValueSource<T>
: CascadingValueSource<T>, IDisposable where T : INotifyPropertyChanged
{
private readonly T state;
private readonly CascadingValueSource<T> source;
public CascadingStateValueSource(T state, bool isFixed = false)
: base(state, isFixed = false)
{
this.state = state;
source = new CascadingValueSource<T>(state, isFixed);
this.state.PropertyChanged += HandlePropertyChanged;
}
private void HandlePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
_ = NotifyChangedAsync();
}
public void Dispose()
{
state.PropertyChanged -= HandlePropertyChanged;
}
}
}
Het type PropertyChangedEventHandler (HandlePropertyChanged
) roept de CascadingValueSource<TValue>-methode van NotifyChangedAsync aan om abonnees op de hoogte te stellen dat de trapsgewijze waarde is gewijzigd. De Task optie wordt verwijderd bij het aanroepen NotifyChangedAsync omdat de oproep alleen de duur van de verzending naar de synchrone context vertegenwoordigt. Uitzonderingen worden intern verwerkt door ze naar de renderer te verzenden binnen de context van het onderdeel dat de uitzondering genereerde bij het ontvangen van de update. Dit is dezelfde manier waarop uitzonderingen worden verwerkt met een CascadingValue<TValue>, die niet op de hoogte wordt gesteld van uitzonderingen die plaatsvinden binnen meldingsontvangers. De gebeurtenis-handler wordt losgekoppeld in de Dispose
methode om een geheugenlek te voorkomen.
In het Program
bestand wordt NotifyingDalek
doorgegeven, waarmee een CascadingValueSource<TValue> met een initiële Unit
waarde van 888 eenheden wordt gecreëerd.
builder.Services.AddNotifyingCascadingValue(new NotifyingDalek() { Units = 888 });
Notitie
Voor een Blazor Web App oplossing die bestaat uit server- en clientprojecten (.Client
) wordt de voorgaande code in het bestand van Program
elk project geplaatst.
Het volgende onderdeel wordt gebruikt om te demonstreren hoe het wijzigen van de waarde van NotifyingDalek.Units
abonnees meldt.
Daleks.razor
:
<h2>Daleks component</h2>
<div>
<b>Dalek Units:</b> @Dalek?.Units
</div>
<div>
<label>
<span style="font-weight:bold">New Unit Count:</span>
<input @bind="dalekCount" />
</label>
<button @onclick="Update">Update</button>
</div>
<div>
<button @onclick="SetOneThousandUnits">Set Units to 1,000</button>
</div>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
private int dalekCount;
[CascadingParameter]
public NotifyingDalek? Dalek { get; set; }
private void Update()
{
if (Dalek is not null)
{
Dalek.Units = dalekCount;
dalekCount = 0;
}
}
private async Task SetOneThousandUnits()
{
if (Dalek is not null)
{
await Dalek.SetUnitsToOneThousandAsync();
}
}
}
Als u meerdere abonneemeldingen wilt demonstreren, worden in het volgende DaleksMain
onderdeel drie Daleks
onderdelen weergegeven. Wanneer het aantal eenheden (Units
) van het ene Dalek
onderdeel wordt bijgewerkt, worden de andere twee Dalek
onderdeelabonnees bijgewerkt.
DaleksMain.razor
:
@page "/daleks-main"
<PageTitle>Daleks Main</PageTitle>
<h1>Daleks Main</h1>
<Daleks />
<Daleks />
<Daleks />
Voeg een navigatiekoppeling toe aan het DaleksMain
onderdeel in NavMenu.razor
:
<div class="nav-item px-3">
<NavLink class="nav-link" href="daleks-main">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Daleks
</NavLink>
</div>
Omdat het CascadingValueSource<TValue>type in dit voorbeeld (NotifyingDalek
) een klassetype is, kunt u vrijwel elke vereiste voor functiespecificatie voor statusbeheer voldoen. Abonnementen maken echter overhead en verminderen de prestaties, dus benchmark de prestaties van deze benadering in uw app en vergelijk deze met andere statusbeheermethoden voordat ze in een productie-app worden gebruikt met beperkte verwerking en geheugenbronnen.
Elke verandering in de toestand (elke wijziging van de eigenschapswaarde van de klasse) zorgt ervoor dat alle geabonneerde componenten opnieuw worden gerenderd, ongeacht welk deel van de toestand ze gebruiken. Vermijd het maken van één grote klasse die de hele globale toepassingsstatus vertegenwoordigt. Maak in plaats daarvan gedetailleerde klassen en cascadeer deze afzonderlijk met specifieke abonnementen op cascaderende parameters, zodat alleen componenten die zich hebben geabonneerd op een specifiek deel van de applicatiestatus worden beïnvloed door wijzigingen.
CascadingValue
-onderdeel
Een vooroudercomponent biedt een trapsgewijze waarde met behulp van het Blazor framework CascadingValue
component, dat een subboom van een componentenhiërarchie omvat en één waarde levert aan alle componenten binnen zijn subboom.
In het volgende voorbeeld ziet u de stroom van themagegevens in de componentenhiërarchie om een CSS-stijlklasse aan knoppen in onderliggende componenten toe te wijzen.
De volgende ThemeInfo
C#-klasse geeft de themagegevens op.
Notitie
Voor de voorbeelden in deze sectie is de naamruimte van de app BlazorSample
. Wanneer u experimenteert met de code in uw eigen voorbeeld-app, wijzigt u de naamruimte van de app in de naamruimte van uw voorbeeld-app.
ThemeInfo.cs
:
namespace BlazorSample;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
Het volgende indelingsonderdeel geeft themainformatie (ThemeInfo
) op als een trapsgewijze waarde voor alle componenten die samen het layoutlichaam van de Body eigenschap vormen.
ButtonClass
wordt een waarde van btn-success
toegewezen. Dit is een Bootstrap-knopstijl. Elk afstammend onderdeel in de componenthiërarchie kan de eigenschap ButtonClass
gebruiken via de ThemeInfo
trapsgewijze waarde.
MainLayout.razor
:
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<CascadingValue Value="@theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
</main>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<CascadingValue Value="@theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</main>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<CascadingValue Value="@theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</div>
</div>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
}
@inherits LayoutComponentBase
@using BlazorSample.UIThemeClasses
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<CascadingValue Value="theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
</div>
@code {
private ThemeInfo theme = new ThemeInfo { ButtonClass = "btn-success" };
}
Blazor Web Appbieden alternatieve benaderingen voor trapsgewijze waarden die breder van toepassing zijn op de app dan het inrichten ervan via één indelingsbestand:
Verpakt de opmaak van het
Routes
onderdeel in eenCascadingValue
-onderdeel om de gegevens op te geven als trapsgewijze waarde voor alle onderdelen van de app.In het volgende voorbeeld worden
ThemeInfo
-gegevens trapsgewijs van hetRoutes
-onderdeel afgeleid.Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Notitie
Het verpakken van het
Routes
-componentexemplaar in hetApp
-component (Components/App.razor
) met eenCascadingValue
-component wordt niet ondersteund.Geef een cascaderende waarde op op hoofdniveau als een service door de extensiemethode AddCascadingValue aan te roepen op de servicecollectiebouwer.
In het volgende voorbeeld worden
ThemeInfo
-gegevens trapsgewijs afkomstig uit hetProgram
-bestand.Program.cs
builder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Zie de volgende secties van dit artikel voor meer informatie:
kenmerk [CascadingParameter]
Als u trapsgewijze waarden wilt gebruiken, declareren afstammende onderdelen trapsgewijze parameters met behulp van het [CascadingParameter]
kenmerk. Trapsgewijze waarden zijn per typegebonden aan trapsgewijze parameters
Met het volgende onderdeel wordt de ThemeInfo
trapsgewijze waarde gekoppeld aan een trapsgewijze parameter, eventueel met dezelfde naam van ThemeInfo
. De parameter wordt gebruikt om de CSS-klasse in te stellen voor de knop Increment Counter (Themed)
.
ThemedCounter.razor
:
@page "/themed-counter"
<PageTitle>Themed Counter</PageTitle>
<h1>Themed Counter Example</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount() => currentCount++;
}
@page "/themed-counter"
<PageTitle>Themed Counter</PageTitle>
<h1>Themed Counter Example</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount() => currentCount++;
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button
class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)"
@onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo? ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses
<h1>Themed Counter</h1>
<p>Current count: @currentCount</p>
<p>
<button @onclick="IncrementCount">
Increment Counter (Unthemed)
</button>
</p>
<p>
<button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
Increment Counter (Themed)
</button>
</p>
@code {
private int currentCount = 0;
[CascadingParameter]
protected ThemeInfo ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
Net als bij een normale onderdeelparameter worden onderdelen die een trapsgewijze parameter accepteren, aangepast wanneer de trapsgewijze waarde wordt gewijzigd. Als u bijvoorbeeld een ander thema-exemplaar configureert, wordt het ThemedCounter
-onderdeel uit het CascadingValue
-onderdeel-sectie opnieuw gerenderd.
MainLayout.razor
:
<main>
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<CascadingValue Value="theme">
<article class="content px-4">
@Body
</article>
</CascadingValue>
<button @onclick="ChangeToDarkTheme">Dark mode</button>
</main>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
private void ChangeToDarkTheme()
{
theme = new() { ButtonClass = "btn-secondary" };
}
}
CascadingValue<TValue>.IsFixed kan worden gebruikt om aan te geven dat een trapsgewijze parameter niet wordt gewijzigd na initialisatie.
Trapsgewijze waarden/parameters en grenzen van de rendermodus
Trapsgewijze parameters geven geen gegevens door over de grenzen van de rendermodus:
Interactieve sessies worden uitgevoerd in een andere context dan de pagina's die gebruikmaken van statische server-side rendering (statische SSR). Er is geen vereiste dat de server die de pagina produceert, zelfs dezelfde computer is die als host fungeert voor een latere Interactieve Server-sessie, waaronder voor WebAssembly-onderdelen waarbij de server een andere computer is dan de client. Het voordeel van statische server-side rendering (statische SSR) is het bereiken van de volledige prestaties van pure stateless HTML-rendering.
De status die de grens tussen statische en interactieve rendering overschrijdt, moet serialiseerbaar zijn. Onderdelen zijn willekeurige objecten die verwijzen naar een enorme keten van andere objecten, waaronder de renderer, de DI-container en elk DI-service-exemplaar. U moet ervoor zorgen dat de status expliciet vanuit statische SSR wordt geserialiseerd om deze beschikbaar te maken in volgende interactief gegenereerde onderdelen. Er worden twee benaderingen aangenomen:
- Via het Blazor-framework worden parameters die via een statische SSR naar een interactieve renderinggrens worden doorgegeven, automatisch geserialiseerd als ze JSON-serializeerbaar zijn of als er een fout optreedt.
- Toestand die is opgeslagen in permanente onderdeelstatus wordt automatisch geserialiseerd en hersteld als deze JSON-serialiseerbaar is, of een fout wordt opgeworpen.
Trapsgewijze parameters zijn niet JSON-serializeerbaar omdat de typische gebruikspatronen voor trapsgewijze parameters enigszins lijken op DI-services. Er zijn vaak platformspecifieke varianten van trapsgewijze parameters, dus het zou niet nuttig zijn voor ontwikkelaars als het framework hen zou verhinderen om server-interactieve versies of WebAssembly-specifieke versies te hebben. Bovendien zijn veel trapsgewijze parameterwaarden in het algemeen niet serialiseerbaar, dus het zou onpraktisch zijn om bestaande apps bij te werken als u zou moeten stoppen met het gebruik van alle niet-serialiseerbare trapsgewijze parameterwaarden.
Aanbevelingen:
Als u status beschikbaar wilt maken voor alle interactieve onderdelen als trapsgewijze parameter, raden we u aan trapsgewijze waarden of trapsgewijze waarden op hoofdniveau te gebruiken met meldingen. Er is een factory-patroon beschikbaar en de app kan bijgewerkte waarden verzenden na het opstarten van de app. Trapsgewijze waarden op hoofdniveau zijn beschikbaar voor alle onderdelen, inclusief interactieve onderdelen, omdat ze worden verwerkt als DI-services.
Voor auteurs van onderdeelbibliotheken kunt u een uitbreidingsmethode maken voor een bibliotheekgebruiker die vergelijkbaar is met de volgende:
builder.Services.AddLibraryCascadingParameters();
Instrueer ontwikkelaars om uw extensiemethode aan te roepen. Dit is een goed alternatief voor hen instrueren om een
<RootComponent>
-onderdeel aan hunMainLayout
-onderdeel toe te voegen.
Meerdere waarden in cascade
Als u meerdere waarden van hetzelfde type in dezelfde substructuur wilt trapsgewijs toepassen, geeft u een unieke Name tekenreeks op voor elk CascadingValue
onderdeel en de bijbehorende [CascadingParameter]
kenmerken.
In het volgende voorbeeld worden twee CascadingValue
onderdelen trapsgewijs verschillende exemplaren van CascadingType
:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
In een afstammelingsonderdeel ontvangen de trapsgewijze parameters hun trapsgewijze waarden van het bovenliggende onderdeel door Name:
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Gegevens doorgeven in een onderdeelhiërarchie
Met trapsgewijze parameters kunnen onderdelen ook gegevens doorgeven in een onderdeelhiërarchie. Bekijk het volgende voorbeeld van een gebruikersinterfacetabset, waarbij een tabbladensetonderdeel een reeks afzonderlijke tabbladen onderhoudt.
Notitie
Voor de voorbeelden in deze sectie is de naamruimte van de app BlazorSample
. Wanneer u experimenteert met de code in uw voorbeeld-app, wijzigt u de namespace in de namespace van uw voorbeeld-app.
Maak een ITab
-interface die tabbladen implementeren in een map met de naam UIInterfaces
.
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Notitie
Zie RenderFragmentvoor meer informatie over Razor.
Het volgende TabSet
onderdeel onderhoudt een set tabbladen. De Tab
onderdelen van de tabset, die verderop in deze sectie worden gemaakt, leveren de lijstitems (<li>...</li>
) op voor de lijst (<ul>...</ul>
).
Subcomponenten van Tab
worden niet expliciet als parameters aan de TabSet
doorgegeven. In plaats daarvan maken de Tab
-componenten deel uit van de inhoud van de TabSet
.
TabSet
Er is echter nog steeds een verwijzing naar elk Tab
onderdeel nodig, zodat de headers en het actieve tabblad kunnen worden weergegeven. Om deze coördinatie mogelijk te maken zonder dat er extra code is vereist, kan het TabSet
onderdeel zichzelf als een trapsgewijze waarde bieden die vervolgens wordt opgehaald door de afstammende Tab
onderdelen.
TabSet.razor
:
@using BlazorSample.UIInterfaces
<!-- Display the tab headers -->
<CascadingValue Value="this">
<ul class="nav nav-tabs">
@ChildContent
</ul>
</CascadingValue>
<!-- Display body for only the active tab -->
<div class="nav-tabs-body p-4">
@ActiveTab?.ChildContent
</div>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
public ITab? ActiveTab { get; private set; }
public void AddTab(ITab tab)
{
if (ActiveTab is null)
{
SetActiveTab(tab);
}
}
public void SetActiveTab(ITab tab)
{
if (ActiveTab != tab)
{
ActiveTab = tab;
StateHasChanged();
}
}
}
Afgeleide Tab
-onderdelen nemen de omvattende TabSet
op als een trapsgewijze parameter. De Tab
onderdelen voegen zichzelf toe aan de TabSet
en coördineren om het actieve tabblad in te stellen.
Tab.razor
:
@using BlazorSample.UIInterfaces
@implements ITab
<li>
<a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
@Title
</a>
</li>
@code {
[CascadingParameter]
public TabSet? ContainerTabSet { get; set; }
[Parameter]
public string? Title { get; set; }
[Parameter]
public RenderFragment? ChildContent { get; set; }
private string? TitleCssClass =>
ContainerTabSet?.ActiveTab == this ? "active" : null;
protected override void OnInitialized()
{
ContainerTabSet?.AddTab(this);
}
private void ActivateTab()
{
ContainerTabSet?.SetActiveTab(this);
}
}
Het volgende ExampleTabSet
onderdeel maakt gebruik van het TabSet
-onderdeel, dat drie Tab
onderdelen bevat.
ExampleTabSet.razor
:
@page "/example-tab-set"
<TabSet>
<Tab Title="First tab">
<h4>Greetings from the first tab!</h4>
<label>
<input type="checkbox" @bind="showThirdTab" />
Toggle third tab
</label>
</Tab>
<Tab Title="Second tab">
<h4>Hello from the second tab!</h4>
</Tab>
@if (showThirdTab)
{
<Tab Title="Third tab">
<h4>Welcome to the disappearing third tab!</h4>
<p>Toggle this tab from the first tab.</p>
</Tab>
}
</TabSet>
@code {
private bool showThirdTab;
}