ASP.NET Core Blazor sammanhängande värden och parametrar
Varning
Den här versionen av ASP.NET Core stöds inte längre. Mer information finns i .NET och .NET Core Support Policy. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Viktig
Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.
Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.
Den här artikeln beskriver hur du flödar data från en överordnad Razor komponent till underordnade komponenter.
sammanhängande värden och parametrar ger ett bekvämt sätt att flöda data ned i en komponenthierarki från en överordnad komponent till valfritt antal underordnade komponenter. Till skillnad från komponentparametrarkräver sammanhängande värden och parametrar inte någon attributtilldelning för varje underordnad komponent där data används. Sammanhängande värden och parametrar gör det också möjligt för komponenter att samordna med varandra i en komponenthierarki.
Not
Kodexemplen i den här artikeln använder nullbara referenstyper (NRT) och .NET-kompilatorn null-state static analysis, som stöds i ASP.NET Core i .NET 6 eller senare. När du riktar in dig på ASP.NET Core 5.0 eller tidigare tar du bort null-typbeteckningen (?
) från CascadingType?
, @ActiveTab?
, RenderFragment?
, ITab?
, TabSet?
och string?
typer i artikelns exempel.
Sammanhängande värden på rotnivå
Sammanhängande värden på rotnivå kan registreras för hela komponenthierarkin. Namngivna sammanhängande värden och prenumerationer för uppdateringsmeddelanden stöds.
Följande klass används i det här avsnittets exempel.
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; }
}
Följande registreringar görs i appens Program
-fil med AddCascadingValue:
-
Dalek
med ett egenskapsvärde förUnits
registreras som ett fast sammanhängande värde. - En andra
Dalek
registrering med ett annat egenskapsvärde förUnits
heter "AlphaGroup
".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
Följande Daleks
komponent visar de överlappande värdena.
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; }
}
I följande exempel registreras Dalek
som ett sammanhängande värde med hjälp av CascadingValueSource<T>
, där <T>
är typen. Flaggan isFixed
anger om värdet är fast. Om det är falskt prenumererar alla mottagare på uppdateringsmeddelanden, som utfärdas genom att anropa NotifyChangedAsync. Prenumerationer skapar omkostnader och minskar prestanda, så ställ in isFixed
på true
om värdet inte ändras.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Varning
Registrering av en komponenttyp som ett sammanhängande värde på rotnivå registrerar inte ytterligare tjänster för typen eller tillåter tjänstaktivering i komponenten.
Behandla nödvändiga tjänster separat från kaskadvärden och registrera dem separat från kaskadtypen.
Undvik att använda AddCascadingValue för att registrera en komponenttyp som ett sammanhängande värde. Omslut i stället <Router>...</Router>
i komponenten Routes
(Components/Routes.razor
) med komponenten och implementera global interaktiv återgivning på serversidan (interaktiv SSR). Ett exempel finns i avsnittet CascadingValue
komponent.
CascadingValue
komponent
En överordnad komponent ger ett sammanhängande värde med hjälp av Blazor ramverkets CascadingValue
komponent, som omsluter ett underträd i en komponenthierarki och tillhandahåller ett enda värde till alla komponenter i dess underträd.
I följande exempel visas flödet av temainformation i komponenthierarkin för att tillhandahålla en CSS-stilklass till knappar i underordnade komponenter.
Följande ThemeInfo
C#-klass anger temainformationen.
Not
För exemplen i det här avsnittet är appens namnområde BlazorSample
. När du experimenterar med koden i din egen exempelapp ändrar du appens namnområde till exempelappens namnområde.
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; }
}
}
Följande layoutkomponent anger temainformation (ThemeInfo
) som ett sammanhängande värde för alla komponenter som utgör layouttexten för egenskapen Body.
ButtonClass
tilldelas värdet btn-success
, vilket är en Bootstrap-knappstil. Alla underordnade komponenter i komponenthierarkin kan använda egenskapen ButtonClass
via det ThemeInfo
sammanhängande värdet.
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 Appger alternativa metoder för värden i en kaskad som gäller mer allmänt för appen än att tillhandahålla dem via en enda layoutfil.
Omslut
Routes
komponenten i enCascadingValue
komponent för att ange data som ett sammanhängande värde för alla appens komponenter.Följande exempel överlappar
ThemeInfo
data från komponentenRoutes
.Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Notis
Det är inte möjligt att omsluta komponentinstansen
Routes
i komponentenApp
(Components/App.razor
) med enCascadingValue
-komponent.Ange ett sammanhängande värde på rotnivå som en tjänst genom att anropa AddCascadingValue-tilläggsmetoden i servicesamlingsverktyget.
I följande exempel kaskaderar data från
ThemeInfo
-filen tillProgram
-filen.Program.cs
builder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Mer information finns i följande avsnitt i den här artikeln:
[CascadingParameter]
-attributet
Om du vill använda sammanhängande värden deklarerar underordnade komponenter sammanhängande parametrar med hjälp av attributet [CascadingParameter]
. Sammanhängande värden är bundna till sammanhängande parametrar efter typ. Sammanhängande flera värden av samma typ beskrivs i avsnittet Cascade multiple values senare i den här artikeln.
Följande komponent binder det kaskaderande värdet ThemeInfo
till en kaskaderande parameter, valfritt med samma namn som ThemeInfo
. Parametern används för att ange CSS-klassen för knappen 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++;
}
}
På samma sätt som en vanlig komponentparameter återskapas komponenter som accepterar en sammanhängande parameter när det sammanhängande värdet ändras. Om du till exempel konfigurerar en annan temainstans orsakar det att ThemedCounter
-komponenten från komponent CascadingValue
sektionen återskapas.
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 användas för att indikera att en sammanhängande parameter inte ändras efter initieringen.
Sammanhängande värden/parametrar och återgivningslägesgränser
Sammanhängande parametrar skickar inte data över gränserna för återgivningsläge:
Interaktiva sessioner körs i en annan kontext än de sidor som använder statisk återgivning på serversidan (statisk SSR). Det finns inget krav på att servern som producerar sidan ens är samma dator som är värd för en senare interaktiv serversession, inklusive för WebAssembly-komponenter där servern är en annan dator än klienten. Fördelen med statisk återgivning på serversidan (statisk SSR) är att få full prestanda för ren tillståndslös HTML-rendering.
Tillstånd som korsar gränsen mellan statisk och interaktiv återgivning måste vara serialiserbart. Komponenter är godtyckliga objekt som refererar till en omfattande kedja av andra objekt, inklusive renderaren, DI-containern och varje DI-tjänstinstans. Du måste uttryckligen orsaka att tillståndet serialiseras från statisk SSR för att göra det tillgängligt i efterföljande interaktivt renderade komponenter. Två metoder används:
- Via det Blazor ramverket serialiseras parametrar som skickas över en statisk SSR till en interaktiv återgivningsgräns automatiskt om de är JSON-serialiserbara eller om ett fel utlöses.
- Tillståndet som lagras i
PersistentComponentState
serialiseras och återställs automatiskt om det är JSON-serialiserbart, eller om ett fel utlöses.
Sammanhängande parametrar är inte JSON-serialiserbara eftersom de typiska användningsmönstren för sammanhängande parametrar liknar DI-tjänster. Det finns ofta plattformsspecifika varianter av sammanhängande parametrar, så det skulle inte vara till någon hjälp för utvecklare om ramverket hindrade utvecklare från att ha server-interaktiva specifika versioner eller WebAssembly-specifika versioner. Dessutom är många sammanhängande parametervärden i allmänhet inte serialiserbara, så det skulle vara opraktiskt att uppdatera befintliga appar om du var tvungen att sluta använda alla icke-numeriska sammanhängande parametervärden.
Rekommendationer:
Om du behöver göra tillståndet tillgängligt för alla interaktiva komponenter som en sammanhängande parameter rekommenderar vi att du använder sammanhängande värden på rotnivå. Ett fabriksmönster är tillgängligt och appen kan generera uppdaterade värden efter appstart. Sammanhängande värden på rotnivå är tillgängliga för alla komponenter, inklusive interaktiva komponenter, eftersom de bearbetas som DI-tjänster.
För komponentbiblioteksförfattare kan du skapa en tilläggsmetod för bibliotekskonsumenter som liknar följande:
builder.Services.AddLibraryCascadingParameters();
Instruera utvecklare att anropa tilläggsmetoden. Det här är ett bra alternativ till att instruera dem att lägga till en
<RootComponent>
komponent i sinMainLayout
komponent.
Kaskadera flera värden
Om du vill överlappa flera värden av samma typ i samma underträd anger du en unik Name sträng för varje CascadingValue
komponent och motsvarande [CascadingParameter]
attribut.
I följande exempel överlappar två CascadingValue
komponenter olika instanser av CascadingType
:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
I en underordnad komponent tar de kaskadparametrarna emot sina kaskadvärden från den överordnade komponenten via Name:
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Skicka data över en komponenthierarki
Sammanhängande parametrar gör det också möjligt för komponenter att skicka data över en komponenthierarki. Överväg följande exempel på flikuppsättningen för användargränssnittet, där en flikuppsättningskomponent underhåller en serie enskilda flikar.
Obs
För exemplen i det här avsnittet är appens namnområde BlazorSample
. När du experimenterar med koden i din egen exempelapp ändrar du namnområdet till exempelappens namnområde.
Skapa ett ITab
gränssnitt som flikar implementerar i en mapp med namnet UIInterfaces
.
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Not
Mer information om RenderFragmentfinns i ASP.NET Core Razor-komponenter.
Den följande TabSet
-komponenten håller en uppsättning flikar. Tabbuppsättningens Tab
komponenter, som skapas senare i det här avsnittet, anger listobjekten (<li>...</li>
) för listan (<ul>...</ul>
).
Underordnade Tab
-komponenter passeras inte uttryckligen som parametrar till TabSet
. I stället är komponenterna i Tab
en del av innehållet för TabSet
. Men TabSet
behöver fortfarande en referens varje Tab
komponent så att den kan återge rubrikerna och den aktiva fliken. Om du vill aktivera den här samordningen utan att behöva ytterligare kod kan TabSet
komponenten ange sig själv som ett sammanhängande värde som sedan hämtas av de underordnade Tab
komponenterna.
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();
}
}
}
Underordnade Tab
komponenter avbildar de innehållande TabSet
som en sammanhängande parameter. De Tab
komponenterna lägger till sig själva i TabSet
och samordnar för att ange den aktiva fliken.
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);
}
}
Följande ExampleTabSet
komponent använder komponenten TabSet
, som innehåller tre Tab
komponenter.
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;
}
Ytterligare resurser
ASP.NET Core