ASP.NET valori e parametri di Blazor base
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere Criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 8 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 la destinazione è ASP.NET Core 5.0 o versioni precedenti, rimuovere la designazione di tipo Null (?
) dai CascadingType?
tipi , @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:
Dalek
con un valore della proprietà perUnits
viene registrato come valore a catena fisso.- Una seconda
Dalek
registrazione 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]
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; }
}
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, che vengono rilasciate chiamando NotifyChangedAsync. Le sottoscrizioni creano overhead e riducono le prestazioni, quindi impostate su isFixed
true
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 .
Componente CascadingValue
Un componente predecessore fornisce un valore a catena usando il componente del CascadingValue
framework, che esegue il Blazor 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
Routes
componente in unCascadingValue
componente per specificare i dati come valore a catena per tutti i componenti dell'app.Nell'esempio seguente vengono propagati i
ThemeInfo
dati dalRoutes
componente .Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Nota
Il wrapping dell'istanza
Routes
delApp
componente nel componente (Components/App.razor
) con unCascadingValue
componente 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
ThemeInfo
dati dalProgram
file .Program.cs
builder.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 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]
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++;
}
}
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 in
PersistentComponentState
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. È 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 ASP.NET Componenti di baseRazor.
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, è TabSet
comunque necessario un riferimento a ogni Tab
componente in modo che possa eseguire il rendering delle intestazioni e della scheda attiva. Per abilitare questo coordinamento senza richiedere codice aggiuntivo, il TabSet
componente può fornire se stesso come valore a catena che viene quindi prelevato dai componenti discendenti Tab
.
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]
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);
}
}
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;
}