Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.
Questo articolo illustra come eseguire il flusso di dati da un componente predecessore Razor ai componenti discendenti.
I valori e i parametri a catena offrono un modo pratico per eseguire il flusso dei dati in una gerarchia di componenti da un componente predecessore a un numero qualsiasi di componenti discendenti. A differenza dei parametri component, i valori e i parametri a catena non richiedono un'assegnazione di attributo per ogni componente discendente in cui vengono utilizzati i dati. I valori e i parametri a catena consentono anche ai componenti di coordinarsi tra loro in una gerarchia di componenti.
Nota
Gli esempi di codice in questo articolo adottano tipi di riferimento nullable (NRT) e l'analisi statica dello stato null del compilatore .NET, supportati in ASP.NET Core in .NET 6 o versione successiva. Quando si prende di mira .NET 5 o versioni precedenti, rimuovere la designazione di tipo null (?) dai tipi CascadingType?, @ActiveTab?, RenderFragment?, ITab?, TabSet? e string? negli esempi dell'articolo.
Valori a cascata a livello radice
I valori a catena a livello radice possono essere registrati per l'intera gerarchia dei componenti. Sono supportati valori e sottoscrizioni denominati a catena per le notifiche di aggiornamento.
La classe seguente viene usata negli esempi di questa sezione.
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; }
}
Le registrazioni seguenti vengono effettuate nel file dell'app Program con AddCascadingValue:
-
Dalekcon un valore della proprietà perUnitsviene registrato come valore a catena fisso. - Una seconda
Dalekregistrazione con un valore di proprietà diverso perUnitsè denominata "AlphaGroup".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
Nel componente seguente Daleks vengono visualizzati i valori a catena.
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]
private 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]
private Dalek? Dalek { get; set; }
[CascadingParameter(Name = "AlphaGroup")]
public Dalek? AlphaGroupDalek { get; set; }
}
Nell'esempio Dalek seguente viene registrato come valore a catena usando CascadingValueSource<T>, dove <T> è il tipo . Il isFixed flag indica se il valore è fisso. Se false, tutti i destinatari vengono sottoscritti per le notifiche di aggiornamento. Le sottoscrizioni creano overhead e riducono le prestazioni, quindi impostate su isFixedtrue se il valore non cambia.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Avviso
La registrazione di un tipo di componente come valore a catena a livello radice non registra servizi aggiuntivi per il tipo o consente l'attivazione del servizio nel componente.
Gestire i servizi necessari separatamente dai valori a catena, registrandoli separatamente dal tipo a catena.
Evitare di usare AddCascadingValue per registrare un tipo di componente come valore a catena. Al contrario, eseguire il <Router>...</Router> wrapping di Routes nel componente (Components/Routes.razor) con il componente e adottare il rendering sul lato server interattivo globale (SSR interattivo). Per un esempio, vedere la sezione relativa al CascadingValue componente .
Valori a cascata a livello radice con notifiche
La chiamata NotifyChangedAsync a inviare notifiche di aggiornamento può essere usata per segnalare a più Razor sottoscrittori di componenti che un valore a catena è cambiato. Le notifiche non sono possibili per i sottoscrittori che adottano il rendering statico lato server (SSR statico), quindi i sottoscrittori devono adottare una modalità di rendering interattiva.
Nell'esempio seguente:
-
NotifyingDalekimplementa INotifyPropertyChanged per notificare ai client che un valore della proprietà è stato modificato. Quando laUnitsproprietà è impostata, il PropertyChangedEventHandler (PropertyChanged) viene richiamato. - Il
SetUnitsToOneThousandAsyncmetodo può essere attivato dai sottoscrittori per impostareUnitssu 1.000 con un ritardo di elaborazione simulato.
Tenere presente nel codice di produzione che qualsiasi modifica dello stato (qualsiasi modifica del valore della proprietà della classe) causa il ri-rendering di tutti i componenti sottoscritti, indipendentemente da quale parte dello stato venga utilizzata. È consigliabile creare classi granulari, che vengono propagate separatamente con sottoscrizioni specifiche per garantire che solo i componenti sottoscritti a una parte specifica dello stato dell'applicazione siano interessati dalle modifiche.
Nota
Per una Blazor Web App soluzione costituita da progetti server e client (.Client), il file seguente NotifyingDalek.cs viene inserito nel .Client progetto.
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;
}
}
Il seguente CascadingStateServiceCollectionExtensions crea un CascadingValueSource<TValue> da un tipo che implementa INotifyPropertyChanged.
Nota
Per una Blazor Web App soluzione costituita da progetti server e client (.Client), il file seguente CascadingStateServiceCollectionExtensions.cs viene inserito nel .Client progetto.
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;
}
}
}
Il tipo PropertyChangedEventHandler (HandlePropertyChanged) chiama il metodo di CascadingValueSource<TValue>NotifyChangedAsync per notificare ai sottoscrittori che il valore a cascata è stato modificato. L'oggetto Task viene rimosso quando si chiama NotifyChangedAsync perché la chiamata rappresenta solo la durata dell'invio al contesto sincrono. Le eccezioni vengono gestite internamente indirizzandole al renderer nel contesto del componente che ha generato l'eccezione durante la ricezione dell'aggiornamento. Si tratta dello stesso modo in cui le eccezioni vengono elaborate con un CascadingValue<TValue>oggetto , che non riceve notifiche sulle eccezioni che si verificano all'interno dei destinatari delle notifiche. Il gestore eventi viene disconnesso nel Dispose metodo per evitare una perdita di memoria.
Nel file Program, NotifyingDalek viene passato per creare un CascadingValueSource<TValue> con un valore iniziale Unit di 888 unità.
builder.Services.AddNotifyingCascadingValue(new NotifyingDalek() { Units = 888 });
Nota
Per una Blazor Web App soluzione costituita da progetti server e client (.Client), il codice sopra menzionato viene inserito nel file Program di ciascun progetto.
Il componente seguente viene usato per illustrare come modificare il valore di NotifyingDalek.Units notifica ai sottoscrittori.
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]
private 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();
}
}
}
Per illustrare più notifiche degli abbonati, il componente seguente DaleksMain renderizza tre Daleks componenti. Quando viene aggiornato il numero di unità (Units) di un Dalek componente, gli altri due Dalek sottoscrittori dei componenti vengono aggiornati.
DaleksMain.razor:
@page "/daleks-main"
<PageTitle>Daleks Main</PageTitle>
<h1>Daleks Main</h1>
<Daleks />
<Daleks />
<Daleks />
Aggiungere un collegamento di navigazione al componente DaleksMain 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>
Poiché in questo esempio il tipo di CascadingValueSource<TValue> (NotifyingDalek) è un tipo di classe, è possibile soddisfare praticamente qualsiasi requisito della specifica delle funzionalità di gestione dello stato. Tuttavia, le sottoscrizioni creano overhead e riducono le prestazioni, quindi confrontano le prestazioni di questo approccio nella tua app e confrontalo con altri approcci di gestione dello stato prima di adottarlo in un'app di produzione con risorse di elaborazione e memoria limitate.
Qualsiasi modifica dello stato (qualsiasi variazione del valore di una proprietà della classe) fa sì che tutti i componenti registrati vengano rindirizzati, indipendentemente dalla parte dello stato utilizzata. Evitare di creare una singola classe di grandi dimensioni che rappresenta l'intero stato dell'applicazione globale. Creare invece classi granulari e propagarle separatamente con sottoscrizioni specifiche ai parametri a catena, assicurandosi che solo i componenti sottoscritti a una parte specifica dello stato dell'applicazione siano interessati dalle modifiche.
Componente CascadingValue
Un componente predecessore fornisce un valore a catena usando il componente del Blazor framework, che esegue il CascadingValue wrapping di un sottoalbero di una gerarchia di componenti e fornisce un singolo valore a tutti i componenti all'interno del relativo sottoalbero.
Nell'esempio seguente viene illustrato il flusso delle informazioni sul tema nella gerarchia dei componenti per fornire una classe di stile CSS ai pulsanti nei componenti figlio.
La classe C# seguente ThemeInfo specifica le informazioni sul tema.
Nota
Per gli esempi in questa sezione, lo spazio dei nomi dell'app è BlazorSample. Quando si sperimenta il codice nella propria app di esempio, modificare lo spazio dei nomi dell'app nello spazio dei nomi dell'app di esempio.
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; }
}
}
Il componente di layout seguente specifica le informazioni sul tema (ThemeInfo) come valore a catena per tutti i componenti che costituiscono il corpo del layout della Body proprietà.
ButtonClass viene assegnato un valore di btn-success, che è uno stile pulsante Bootstrap. Qualsiasi componente discendente nella gerarchia dei componenti può usare la ButtonClass proprietà tramite il ThemeInfo valore a catena.
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 Apps forniscono approcci alternativi per i valori a catena che si applicano più ampiamente all'app rispetto all'arredamento tramite un unico file di layout:
Eseguire il wrapping del markup del
Routescomponente in unCascadingValuecomponente per specificare i dati come valore a catena per tutti i componenti dell'app.Nell'esempio seguente vengono propagati i
ThemeInfodati dalRoutescomponente .Routes.razor:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }Nota
Il wrapping dell'istanza
RoutesdelAppcomponente nel componente (Components/App.razor) con unCascadingValuecomponente non è supportato.Specificare un valore a catena a livello radice come servizio chiamando il AddCascadingValue metodo di estensione nel generatore di raccolte di servizi.
Nell'esempio seguente vengono propagati i
ThemeInfodati dalProgramfile .Program.csbuilder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Per altre informazioni, vedere le sezioni seguenti di questo articolo:
Attributo [CascadingParameter]
Per usare i valori a catena, i componenti discendenti dichiarano parametri a catena usando l'attributo [CascadingParameter]. I valori a catena sono associati a parametri a catena per tipo. La propagazione di più valori dello stesso tipo è descritta nella sezione Cascade multiple values più avanti in questo articolo.
Il private modificatore di accesso è consigliato per i parametri a catena perché il parametro deve essere usato solo all'interno della classe del componente nella maggior parte dei casi. Quando è necessaria la sottoclasse, usare il protected modificatore di accesso.
Il componente seguente associa il ThemeInfo valore a catena a un parametro a catena, facoltativamente usando lo stesso nome di ThemeInfo. Il parametro viene usato per impostare la classe CSS per il Increment Counter (Themed) pulsante.
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]
private 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]
private 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]
private 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]
private 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]
private 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]
private ThemeInfo ThemeInfo { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
Analogamente a un parametro di componente normale, i componenti che accettano un parametro a catena vengono rirenderati quando viene modificato il valore a catena. Ad esempio, la configurazione di un'istanza del tema diversa causa il ripristino del ThemedCounter componente dalla sezione del CascadingValue componente .
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 può essere usato per indicare che un parametro a catena non cambia dopo l'inizializzazione.
Valori/parametri a catena e limiti della modalità di rendering
I parametri a catena non passano i dati tra i limiti della modalità di rendering:
Le sessioni interattive vengono eseguite in un contesto diverso rispetto alle pagine che usano il rendering statico lato server (SSR statico). Non è necessario che il server che produce la pagina sia anche lo stesso computer che ospita alcune sessioni successive di Interactive Server, incluso per i componenti WebAssembly in cui il server è un computer diverso per il client. Il vantaggio del rendering statico lato server (SSR statico) è quello di ottenere le prestazioni complete del rendering HTML senza stato puro.
Lo stato che supera il limite tra il rendering statico e interattivo deve essere serializzabile. I componenti sono oggetti arbitrari che fanno riferimento a una vasta catena di altri oggetti, tra cui il renderer, il contenitore DI e ogni istanza del servizio di inserimento delle dipendenze. È necessario fare in modo esplicito che lo stato venga serializzato da SSR statico per renderlo disponibile nei componenti visualizzati in modo interattivo successivo. Vengono adottati due approcci:
- Tramite il Blazor framework, i parametri passati attraverso un ssrio statico al limite di rendering interattivo vengono serializzati automaticamente se sono serializzabili in JSON o viene generato un errore.
- Lo stato archiviato nello stato del componente persistente viene serializzato e ripristinato automaticamente se è serializzabile in JSON o viene generato un errore.
I parametri a catena non sono serializzabili in JSON perché i modelli di utilizzo tipici per i parametri a catena sono in qualche modo simili ai servizi di inserimento delle dipendenze. Spesso ci sono varianti specifiche della piattaforma di parametri a catena, quindi sarebbe inutili per gli sviluppatori se il framework impedisse agli sviluppatori di avere versioni specifiche del server o versioni specifiche di WebAssembly. Inoltre, molti valori di parametro a catena in generale non sono serializzabili, quindi sarebbe poco pratico aggiornare le app esistenti se si dovesse interrompere l'uso di tutti i valori dei parametri nonerializzabili a catena.
Raccomandazioni:
Se è necessario rendere disponibile lo stato per tutti i componenti interattivi come parametro a catena, è consigliabile usare valori a catena a livello radice o valori a catena a livello radice con notifiche. È disponibile un modello factory e l'app può generare valori aggiornati dopo l'avvio dell'app. I valori a catena a livello radice sono disponibili per tutti i componenti, inclusi i componenti interattivi, poiché vengono elaborati come servizi di inserimento delle dipendenze.
Per gli autori di librerie di componenti, è possibile creare un metodo di estensione per i consumer di libreria in modo simile al seguente:
builder.Services.AddLibraryCascadingParameters();Indicare agli sviluppatori di chiamare il metodo di estensione. Si tratta di un'alternativa audio per indicare loro di aggiungere un
<RootComponent>componente nel componenteMainLayout.
Più valori a catena
Per propagare più valori dello stesso tipo all'interno dello stesso sottoalbero, specificare una stringa univoca Name per ogni CascadingValue componente e i relativi attributi.[CascadingParameter]
Nell'esempio seguente due CascadingValue componenti si sovrapporno a istanze diverse di CascadingType:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
In un componente discendente, i parametri a catena ricevono i valori a catena dal componente predecessore da Name:
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Passare i dati attraverso una gerarchia di componenti
I parametri a catena consentono anche ai componenti di passare i dati in una gerarchia di componenti. Si consideri l'esempio di set di schede dell'interfaccia utente seguente, in cui un componente del set di schede gestisce una serie di singole schede.
Nota
Per gli esempi in questa sezione, lo spazio dei nomi dell'app è BlazorSample. Quando si sperimenta il codice nella propria app di esempio, modificare lo spazio dei nomi dell'app di esempio nello spazio dei nomi dell'app di esempio.
Creare un'interfaccia ITab implementata da schede in una cartella denominata UIInterfaces.
UIInterfaces/ITab.cs:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Nota
Per altre informazioni su RenderFragment, vedere Razor di base.
Il componente seguente TabSet gestisce un set di schede. I componenti del set di Tab schede, creati più avanti in questa sezione, specificano gli elementi di elenco () per l'elenco (<li>...</li><ul>...</ul>).
I componenti figlio Tab non vengono passati in modo esplicito come parametri a TabSet. I componenti figlio Tab fanno invece parte del contenuto figlio di TabSet. Tuttavia, l'TabSet necessita ancora di un riferimento a ogni componente Tab in modo che possa eseguire il rendering delle intestazioni e della scheda attiva. Per abilitare questo coordinamento senza richiedere codice aggiuntivo, il componente TabSetpuò fornire se stesso come valore a catena che viene quindi prelevato dai componenti Tab discendenti.
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();
}
}
}
I componenti discendenti Tab acquisisce l'oggetto che lo contiene TabSet come parametro a catena. I Tab componenti si aggiungono alla TabSet coordinata e per impostare la scheda attiva.
Tab.razor:
@using BlazorSample.UIInterfaces
@implements ITab
<li>
<a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
@Title
</a>
</li>
@code {
[CascadingParameter]
private 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);
}
}
Il componente seguente ExampleTabSet usa il TabSet componente , che contiene tre Tab componenti.
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;
}