Blazor ASP.NET základních kaskádových hodnot a parametrů
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Upozorňující
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Důležité
Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Tento článek vysvětluje, jak tok dat z nadřazené Razor komponenty do sestupných komponent.
Kaskádové hodnoty a parametry poskytují pohodlný způsob, jak tok dat v hierarchii komponent z nadřazené komponenty do libovolného počtu sestupných komponent. Na rozdíl od parametrů komponent nevyžadují kaskádové hodnoty a parametry přiřazení atributů pro každou sestupnou komponentu, ve které jsou data spotřebována. Kaskádové hodnoty a parametry také umožňují komponentám vzájemně koordinovat hierarchii komponent.
Poznámka:
Příklady kódu v tomto článku přijímají referenční typy s možnou hodnotou null (NRT) a statickou analýzu stavu null-stav kompilátoru .NET, které jsou podporovány v ASP.NET Core v .NET 6 nebo novější. Při cílení na ASP.NET Core 5.0 nebo starší odeberte označení typu null (?
) z CascadingType?
příkladu článku , @ActiveTab?
, RenderFragment?
, ITab?
, TabSet?
a string?
typy.
Kaskádové hodnoty na kořenové úrovni
Kaskádové hodnoty kořenové úrovně lze zaregistrovat pro celou hierarchii komponent. Podporují se pojmenované kaskádové hodnoty a odběry pro oznámení o aktualizacích.
V příkladech této části se používá následující třída.
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; }
}
V souboru aplikace Program
jsou provedeny následující registrace:AddCascadingValue
-
Dalek
s hodnotou vlastnosti proUnits
je registrován jako pevná kaskádová hodnota. - Druhá
Dalek
registrace s jinou hodnotou vlastnosti máUnits
název "AlphaGroup
".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
Následující Daleks
komponenta zobrazuje kaskádové hodnoty.
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; }
}
V následujícím příkladu Dalek
je registrován jako kaskádová hodnota pomocí CascadingValueSource<T>
, kde <T>
je typ. Příznak isFixed
označuje, jestli je hodnota pevná. Pokud je false, všichni příjemci jsou přihlášeni k odběru oznámení o aktualizacích, které jsou vystaveny voláním NotifyChangedAsync. Předplatná vytvářejí režii a snižují výkon, takže pokud isFixed
se hodnota nezmění, nastavte true
ji.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Upozorňující
Registrace typu komponenty jako kaskádové hodnoty kořenové úrovně neregistruje další služby pro typ nebo povolení aktivace služby v komponentě.
Zacházejte s požadovanými službami odděleně od kaskádových hodnot a zaregistrujte je odděleně od kaskádového typu.
AddCascadingValue Vyhněte se registraci typu komponenty jako kaskádové hodnoty. Místo toho zabalte <Router>...</Router>
komponentu Routes
(Components/Routes.razor
) komponentou a přijměte globální interaktivní vykreslování na straně serveru (interaktivní SSR). Příklad najdete v CascadingValue
části komponent .
CascadingValue
komponenta
Nadřazená komponenta poskytuje kaskádovou hodnotu pomocí Blazor komponenty architektury CascadingValue
, která zabalí podstrom hierarchie komponent a poskytuje jednu hodnotu všem komponentám v jeho podstromu.
Následující příklad ukazuje tok informací o motivu v hierarchii komponent, aby poskytoval třídu stylů CSS tlačítkům v podřízených komponentách.
ThemeInfo
Následující třída jazyka C# určuje informace o motivu.
Poznámka:
V příkladech v této části je BlazorSample
obor názvů aplikace . Při experimentování s kódem ve vlastní ukázkové aplikaci změňte obor názvů aplikace na obor názvů ukázkové aplikace.
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; }
}
}
Následující komponenta rozložení určuje informace o motivu (ThemeInfo
) jako kaskádovou hodnotu pro všechny komponenty, které tvoří tělo Body rozložení vlastnosti.
ButtonClass
je přiřazena hodnota btn-success
, což je bootstrap styl tlačítka. Libovolná sestupná komponenta v hierarchii komponent může tuto vlastnost použít ButtonClass
prostřednictvím ThemeInfo
kaskádové hodnoty.
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 poskytují alternativní přístupy k kaskádovým hodnotám, které se v aplikaci používají obecněji než jejich vybavení prostřednictvím jednoho souboru rozložení:
Zabalte kód
Routes
komponenty doCascadingValue
komponenty a určete data jako kaskádovou hodnotu pro všechny komponenty aplikace.Následující příklad kaskáduje
ThemeInfo
data zRoutes
komponenty.Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Poznámka:
Routes
Zabalení instance komponenty doApp
komponenty (Components/App.razor
) komponentouCascadingValue
se nepodporuje.Zadejte kaskádovou hodnotu kořenové úrovně jako službu voláním AddCascadingValue metody rozšíření v tvůrci kolekce služeb.
Následující příklad kaskáduje
ThemeInfo
data zeProgram
souboru.Program.cs
builder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Další informace najdete v následujících částech tohoto článku:
Atribut [CascadingParameter]
Chcete-li použít kaskádové hodnoty, sestupné komponenty deklarují kaskádové parametry pomocí atributu[CascadingParameter]
. Kaskádové hodnoty jsou vázané na kaskádové parametry podle typu. Kaskádové více hodnot stejného typu je popsáno v části Kaskádové více hodnot dále v tomto článku.
Následující komponenta ThemeInfo
vytvoří vazbu kaskádové hodnoty na kaskádový parametr, volitelně pomocí stejného názvu ThemeInfo
. Parametr slouží k nastavení třídy CSS pro Increment Counter (Themed)
tlačítko.
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++;
}
}
Podobně jako u běžného parametru komponenty se komponenty, které přijímají kaskádový parametr, změní při změně kaskádové hodnoty znovu. Například konfigurace jiné instance motivu způsobí ThemedCounter
, že se komponenta z oddílu CascadingValue
komponenty znovu vymění.
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 lze použít k označení, že se kaskádový parametr po inicializaci nezmění.
Kaskádové hodnoty/parametry a hranice režimu vykreslování
Kaskádové parametry nepředávají data přes hranice režimu vykreslování:
Interaktivní relace se spouštějí v jiném kontextu než stránky, které používají vykreslování na straně statického serveru (static SSR). Není nutné, aby server, který vytváří stránku, byl dokonce stejný počítač, který hostuje některé pozdější relace Interaktivního serveru, včetně komponent WebAssembly, kde je server jiným počítačem s klientem. Výhodou statického vykreslování na straně serveru (static SSR) je získání plného výkonu čistě bezstavového vykreslování HTML.
Stav překračující hranice mezi statickým a interaktivním vykreslováním musí být serializovatelný. Komponenty jsou libovolné objekty, které odkazují na rozsáhlý řetězec jiných objektů, včetně rendereru, kontejneru DI a každé instance služby DI. Musíte explicitně způsobit serializaci stavu ze statického SSR, aby byl dostupný v následných interaktivně vykreslených komponentách. Přijímají se dva přístupy:
- Blazor Prostřednictvím architektury se parametry předávané přes statickou SSR do interaktivní hranice vykreslování serializují automaticky, pokud jsou serializovatelné ve formátu JSON nebo dojde k chybě.
- Stav uložený v
PersistentComponentState
serializaci a automaticky se obnoví, pokud je serializovatelný ve formátu JSON nebo dojde k chybě.
Kaskádové parametry nejsou serializovatelné ve formátu JSON, protože typické vzory použití kaskádových parametrů jsou trochu podobné službám DI. Často existují varianty kaskádových parametrů specifické pro platformu, takže by pro vývojáře bylo neužitečné, kdyby architektura zastavila vývojáře v tom, aby měli verze specifické pro server nebo webAssembly specifické verze. Mnoho kaskádových hodnot parametrů obecně není serializovatelné, takže by bylo nepraktické aktualizovat existující aplikace, pokud byste museli přestat používat všechny neserializovatelné kaskádové hodnoty parametrů.
Doporučení:
Pokud potřebujete zpřístupnit stav pro všechny interaktivní komponenty jako kaskádový parametr, doporučujeme použít kaskádové hodnoty na kořenové úrovni. Model továrny je k dispozici a aplikace může po spuštění aplikace generovat aktualizované hodnoty. Kaskádové hodnoty na kořenové úrovni jsou k dispozici pro všechny komponenty, včetně interaktivních komponent, protože se zpracovávají jako služby DI.
Pro autory knihoven komponent můžete vytvořit metodu rozšíření pro uživatele knihovny podobně jako v následujícím příkladu:
builder.Services.AddLibraryCascadingParameters();
Dejte vývojářům pokyn, aby volali metodu rozšíření. Jedná se o zvukovou alternativu, která jim dává pokyn, aby do své
<RootComponent>
komponenty přidali komponentuMainLayout
.
Kaskádové více hodnot
Pokud chcete kaskádovat více hodnot stejného typu ve stejném podstromu, zadejte jedinečný Name řetězec pro každou CascadingValue
komponentu a jejich odpovídající [CascadingParameter]
atributy.
V následujícím příkladu dvě CascadingValue
komponenty kaskádují různé instance CascadingType
:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
V potomkové komponentě přijímají kaskádové parametry jejich kaskádové hodnoty z nadřazené komponenty Name:
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Předávání dat napříč hierarchií komponent
Kaskádové parametry také umožňují komponentám předávat data napříč hierarchií komponent. Představte si následující příklad sady karet uživatelského rozhraní, kde komponenta sady karet udržuje řadu jednotlivých karet.
Poznámka:
V příkladech v této části je BlazorSample
obor názvů aplikace . Při experimentování s kódem ve vlastní ukázkové aplikaci změňte obor názvů na obor názvů ukázkové aplikace.
Vytvořte ITab
rozhraní, které karty implementují ve složce s názvem UIInterfaces
.
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Poznámka:
Další informace o RenderFragmentkomponentách RazorASP.NET Core.
Následující TabSet
komponenta udržuje sadu karet. Komponenty sady Tab
karet, které jsou vytvořeny později v této části, zadejte položky seznamu (<li>...</li>
) pro seznam (<ul>...</ul>
).
Podřízené Tab
komponenty nejsou explicitně předány jako parametry .TabSet
Místo toho jsou podřízené Tab
komponenty součástí podřízeného TabSet
obsahu souboru . Stále však potřebuje odkaz na každou TabSet
komponentu, Tab
aby mohl vykreslit záhlaví a aktivní kartu. Pokud chcete tuto koordinaci povolit bez nutnosti dalšího kódu, TabSet
může se komponenta poskytnout jako kaskádová hodnota, která se pak vybere sestupně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();
}
}
}
Descendent Tab
components capture the containing TabSet
as a cascading parameter. Komponenty Tab
se přidají do TabSet
a souřadnice pro nastavení aktivní karty.
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);
}
}
Následující ExampleTabSet
komponenta používá komponentu TabSet
, která obsahuje tři Tab
komponenty.
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;
}