Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
W tym artykule wyjaśniono, jak przepływać dane ze składnika przodka Razor do składników malejących.
Wartości kaskadowe i parametry zapewniają wygodny sposób przepływu danych w dół hierarchii składników ze składnika przodka do dowolnej liczby składników malejących. W przeciwieństwie do parametrów składnika wartości kaskadowe i parametry nie wymagają przypisania atrybutów dla każdego składnika malejącego, w którym są używane dane. Kaskadowe wartości i parametry umożliwiają również składnikom koordynowanie ze sobą między hierarchią składników.
Uwaga
Przykłady kodu w tym artykule przyjmują typy odwołań dopuszczających wartość null (NRTs) i statyczną analizę stanu null kompilatora platformy .NET, które są obsługiwane w programie ASP.NET Core na platformie .NET 6 lub nowszym. W przypadku celowania w platformę .NET 5 lub starsze, usuń oznaczenie typu null (?
) z typów CascadingType?
, @ActiveTab?
, RenderFragment?
, ITab?
, TabSet?
i string?
w przykładach w artykule.
Wartości kaskadowe na poziomie głównym
Wartości kaskadowe na poziomie głównym można zarejestrować dla całej hierarchii składników. Obsługiwane są nazwane wartości kaskadowe i subskrypcje dla powiadomień o aktualizacji.
Poniższa klasa jest używana w przykładach tej sekcji.
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; }
}
Następujące rejestracje są wykonywane w pliku aplikacji Program
za pomocą polecenia AddCascadingValue:
-
Dalek
z wartością właściwości dlaUnits
jest rejestrowana jako stała wartość kaskadowa. - Druga
Dalek
rejestracja z inną wartością właściwości dlaUnits
ma nazwę "AlphaGroup
".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });
Daleks
Poniższy składnik wyświetla kaskadowe wartości.
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; }
}
W poniższym przykładzie Dalek
jest rejestrowana jako wartość kaskadowa przy użyciu metody CascadingValueSource<T>
, gdzie <T>
jest typem. Flaga isFixed
wskazuje, czy wartość jest stała. Jeśli false
, wszyscy adresaci są zapisani do otrzymywania powiadomień o aktualizacjach. Subskrypcje tworzą obciążenie i zmniejszają wydajność, więc ustaw wartość isFixed
na true
wartość , jeśli wartość nie ulegnie zmianie.
builder.Services.AddCascadingValue(sp =>
{
var dalek = new Dalek { Units = 789 };
var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);
return source;
});
Ostrzeżenie
Zarejestrowanie typu składnika jako wartości kaskadowej na poziomie głównym nie powoduje zarejestrowania dodatkowych usług dla typu lub zezwolenia na aktywację usługi w składniku.
Traktuj wymagane usługi oddzielnie od kaskadowych wartości, rejestrując je oddzielnie od typu kaskadowego.
Unikaj używania metody , AddCascadingValue aby zarejestrować typ składnika jako wartość kaskadową. Zamiast tego zawijaj element <Router>...</Router>
w składniku Routes
(Components/Routes.razor
) za pomocą składnika i zastosuj globalne interaktywne renderowanie po stronie serwera (interakcyjne SSR). Aby zapoznać się z przykładem, zobacz sekcję CascadingValue
składników .
Wartości kaskadowe na poziomie głównym z powiadomieniami
Wywołanie NotifyChangedAsync w celu wystawienia powiadomień o aktualizacji może być użyte do sygnalizowania wielu Razor subskrybentów komponentów, że wartość kaskadowa uległa zmianie. Powiadomienia nie są możliwe dla subskrybentów, którzy przyjmują statyczne renderowanie po stronie serwera (statyczne SSR), więc subskrybenci muszą przyjąć tryb renderowania interaktywnego.
W poniższym przykładzie:
-
NotifyingDalek
implementuje INotifyPropertyChanged w celu powiadomienia klientów o zmianie wartości właściwości. Po ustawieniu właściwościUnits
, wywoływana jest PropertyChangedEventHandler (PropertyChanged
). -
SetUnitsToOneThousandAsync
Metoda może zostać wyzwolona przez subskrybentów, aby ustawić wartośćUnits
1000 z symulowanym opóźnieniem przetwarzania.
Należy pamiętać, że każda zmiana stanu (jakakolwiek zmiana wartości właściwości klasy) powoduje, że wszystkie zarejestrowane komponenty są przeładowane, niezależnie od części stanu, którą wykorzystują. Zalecamy utworzenie szczegółowych klas i niezależne kaskadowanie ich z określonymi subskrypcjami, aby upewnić się, że zmiany mają wpływ tylko na składniki subskrybowane do określonej części stanu aplikacji.
Uwaga
Blazor Web App W przypadku rozwiązania składającego się z projektów serwera i klienta (.Client
) w projekcie NotifyingDalek.cs
znajduje się następujący .Client
plik.
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;
}
}
Poniższy CascadingStateServiceCollectionExtensions
tworzy CascadingValueSource<TValue> z typu, który implementuje INotifyPropertyChanged.
Uwaga
Blazor Web App W przypadku rozwiązania składającego się z projektów serwera i klienta (.Client
) w projekcie CascadingStateServiceCollectionExtensions.cs
znajduje się następujący .Client
plik.
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;
}
}
}
Typ PropertyChangedEventHandler (HandlePropertyChanged
) wywołuje metodę CascadingValueSource<TValue> metody NotifyChangedAsync, aby powiadomić subskrybentów, że wartość kaskadowa uległa zmianie. Element Task jest odrzucany podczas wywoływania NotifyChangedAsync , ponieważ wywołanie reprezentuje tylko czas trwania wysyłania do kontekstu synchronicznego. Wyjątki są obsługiwane wewnętrznie przez przekazanie ich do renderera w kontekście tego składnika, który zgłosił wyjątek podczas odbierania aktualizacji. Jest to taki sam sposób, w jaki wyjątki są przetwarzane za pomocą CascadingValue<TValue>, który nie jest powiadamiany o wyjątkach występujących wewnątrz odbiorców powiadomień. Procedura obsługi zdarzeń jest odłączona w metodzie Dispose
, aby zapobiec wyciekowi pamięci.
W pliku Program
NotifyingDalek
jest przekazywany, aby utworzyć CascadingValueSource<TValue> z początkową wartością Unit
wynoszącą 888 jednostek.
builder.Services.AddNotifyingCascadingValue(new NotifyingDalek() { Units = 888 });
Uwaga
Blazor Web App W przypadku rozwiązania składającego się z projektów serwera i klienta (.Client
) powyższy kod jest umieszczany w pliku każdego projektuProgram
.
Następujący składnik służy do zademonstrowania, jak zmiana wartości NotifyingDalek.Units
powiadamia subskrybentów.
Daleks.razor
:
<h2>Daleks component</h2>
<div>
<b>Dalek Units:</b> @Dalek?.Units
</div>
<div>
<label>
<span style="font-weight:bold">New Unit Count:</span>
<input @bind="dalekCount" />
</label>
<button @onclick="Update">Update</button>
</div>
<div>
<button @onclick="SetOneThousandUnits">Set Units to 1,000</button>
</div>
<p>
Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>
@code {
private int dalekCount;
[CascadingParameter]
public NotifyingDalek? Dalek { get; set; }
private void Update()
{
if (Dalek is not null)
{
Dalek.Units = dalekCount;
dalekCount = 0;
}
}
private async Task SetOneThousandUnits()
{
if (Dalek is not null)
{
await Dalek.SetUnitsToOneThousandAsync();
}
}
}
Aby zademonstrować liczne powiadomienia subskrybentów, poniższy komponent DaleksMain
renderuje trzy komponenty Daleks
. Po zaktualizowaniu liczby jednostek (Units
) jednego składnika (Dalek
), aktualizowani są subskrybenci dwóch pozostałych składników (Dalek
).
DaleksMain.razor
:
@page "/daleks-main"
<PageTitle>Daleks Main</PageTitle>
<h1>Daleks Main</h1>
<Daleks />
<Daleks />
<Daleks />
Dodaj link nawigacyjny do komponentu DaleksMain
w 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>
Ponieważ typ w tym przykładzie CascadingValueSource<TValue>(NotifyingDalek
) jest typem klasy, można spełnić praktycznie dowolne wymaganie specyfikacji funkcji zarządzania stanem. Jednak subskrypcje tworzą obciążenie i zmniejszają wydajność, dlatego przeprowadź test porównawczy wydajności tej metody w aplikacji i porównaj ją z innymi podejściami do zarządzania stanami przed wdrożeniem jej w aplikacji produkcyjnej z ograniczonymi zasobami przetwarzania i pamięci.
Każda zmiana stanu (jakakolwiek zmiana wartości właściwości klasy) powoduje, że wszystkie zasubskrybowane składniki są ponownie renderowane, niezależnie od tego, z której części stanu korzystają. Unikaj tworzenia pojedynczej dużej klasy reprezentującej cały stan aplikacji globalnej. Zamiast tego należy utworzyć szczegółowe klasy i osobno kaskadować je z określonymi subskrypcjami parametrów kaskadowych, zapewniając, że zmiany mają wpływ tylko na komponenty subskrybujące określoną część stanu aplikacji.
CascadingValue
cm6long
Składnik nadrzędny udostępnia kaskadową wartość przy użyciu Blazor składnika platformy CascadingValue
, który opakowuje poddrzewo hierarchii składników i dostarcza jedną wartość do wszystkich składników w ramach jego poddrzewa.
W poniższym przykładzie pokazano przepływ informacji o motywie w dół hierarchii składników, aby udostępnić klasę stylu CSS przyciskom w składnikach podrzędnych.
ThemeInfo
Poniższa klasa języka C# określa informacje o motywie.
Uwaga
W przypadku przykładów w tej sekcji przestrzeń nazw aplikacji to BlazorSample
. Podczas eksperymentowania z kodem we własnej przykładowej aplikacji zmień przestrzeń nazw aplikacji na przestrzeń nazw przykładowej aplikacji.
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; }
}
}
Poniższy składnik układu określa informacje o motywie (ThemeInfo
) jako wartość kaskadową dla wszystkich składników tworzących treść Body układu właściwości.
ButtonClass
ma przypisaną wartość btn-success
typu , która jest stylem przycisku Bootstrap. Każdy składnik malejący w hierarchii składników może używać ButtonClass
właściwości za pośrednictwem ThemeInfo
wartości kaskadowej.
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 zapewniają alternatywne podejścia do kaskadowych wartości, które mają zastosowanie szerzej do aplikacji niż ich wyposażenie za pomocą jednego pliku układu:
Zawijaj znaczniki
Routes
składnika wCascadingValue
składniku, aby określić dane jako kaskadową wartość dla wszystkich składników aplikacji.W poniższym przykładzie
ThemeInfo
dane kaskadoweRoutes
ze składnika.Routes.razor
:<CascadingValue Value="theme"> <Router ...> ... </Router> </CascadingValue> @code { private ThemeInfo theme = new() { ButtonClass = "btn-success" }; }
Uwaga
Zawijanie
Routes
wystąpienia składnika w składnikuApp
(Components/App.razor
) ze składnikiemCascadingValue
nie jest obsługiwane.Określ wartość kaskadową na poziomie głównym jako usługę, wywołując AddCascadingValue metodę rozszerzenia w konstruktorze kolekcji usług.
Poniższy przykład kaskadowo
ThemeInfo
wyświetla dane zProgram
pliku.Program.cs
builder.Services.AddCascadingValue(sp => new ThemeInfo() { ButtonClass = "btn-primary" });
Aby uzyskać więcej informacji, zobacz następujące sekcje tego artykułu:
Atrybut [CascadingParameter]
Aby użyć wartości kaskadowych, składniki malejące deklarują parametry kaskadowe przy użyciu atrybutu [CascadingParameter]
. Wartości kaskadowe są powiązane z parametrami kaskadowymi według typu. Kaskadowe wartości tego samego typu są omówione w sekcji Kaskada wielu wartości w dalszej części tego artykułu.
Poniższy składnik wiąże ThemeInfo
wartość kaskadową z parametrem kaskadowym, opcjonalnie przy użyciu tej samej nazwy ThemeInfo
. Parametr służy do ustawiania klasy CSS dla Increment Counter (Themed)
przycisku.
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++;
}
}
Podobnie jak w przypadku zwykłego parametru składnika, składniki akceptujące parametr kaskadowy są rerenderowane po zmianie wartości kaskadowej. Na przykład skonfigurowanie innego wystąpienia motywu powoduje ThemedCounter
, że składnik z CascadingValue
sekcji składnika ma wartość rerender.
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 Może służyć do wskazania, że parametr kaskadowy nie zmienia się po zainicjowaniu.
Kaskadowe wartości/parametry i granice trybu renderowania
Parametry kaskadowe nie przekazują danych w granicach trybu renderowania:
Sesje interakcyjne są uruchamiane w innym kontekście niż strony korzystające ze statycznego renderowania po stronie serwera (statyczne SSR). Nie ma potrzeby, aby serwer tworzący stronę był nawet tą samą maszyną, która hostuje późniejszą sesję serwera interakcyjnego, w tym składników zestawu WebAssembly, na których serwer jest inną maszyną do klienta. Zaletą renderowania statycznego po stronie serwera (statycznego renderowania SSR) jest uzyskanie pełnej wydajności czystego bezstanowego renderowania HTML.
Stan przekraczania granicy między renderowaniem statycznym i interaktywnym musi być serializowalny. Składniki są dowolnymi obiektami odwołującymi się do rozległego łańcucha innych obiektów, w tym modułu renderującego, kontenera DI i każdego wystąpienia usługi DI. Należy jawnie spowodować serializacji stanu ze statycznego przewodnika SSR, aby udostępnić go w kolejnych składnikach renderowanych interakcyjnie. Przyjęto dwa podejścia:
- Blazor Za pośrednictwem platformy parametry przekazywane przez statyczny przewodnik SSR do interakcyjnej granicy renderowania są serializowane automatycznie, jeśli są serializowalne w formacie JSON lub zgłaszany jest błąd.
- Stan przechowywany w pliku
PersistentComponentState
jest serializowany i odzyskiwane automatycznie, jeśli można go serializować w formacie JSON lub zgłaszany jest błąd.
Parametry kaskadowe nie są serializowalne w formacie JSON, ponieważ typowe wzorce użycia dla parametrów kaskadowych są nieco podobne do usług DI. Często istnieją warianty specyficzne dla platformy dotyczące parametrów kaskadowych, więc byłoby nieprzydatne dla deweloperów, jeśli platforma powstrzymała deweloperów od posiadania wersji specyficznych dla serwera lub wersji specyficznych dla zestawu WebAssembly. Ponadto wiele kaskadowych wartości parametrów w ogóle nie można serializować, więc niepraktyczne byłoby zaktualizowanie istniejących aplikacji, gdyby trzeba było przestać używać wszystkich nieserializowalnych wartości parametrów kaskadowych.
Rekomendacje:
Jeśli musisz udostępnić stan wszystkim składnikom interaktywnym jako parametr kaskadowy, zalecamy użycie wartości kaskadowych na poziomie głównym lub kaskadowych wartości na poziomie głównym z powiadomieniami. Dostępny jest wzorzec fabryki, a aplikacja może emitować zaktualizowane wartości po uruchomieniu aplikacji. Wartości kaskadowe na poziomie głównym są dostępne dla wszystkich składników, w tym składników interaktywnych, ponieważ są przetwarzane jako usługi DI.
W przypadku autorów bibliotek składników można utworzyć metodę rozszerzenia dla użytkowników bibliotek podobnych do następujących:
builder.Services.AddLibraryCascadingParameters();
Poinstruuj deweloperów, aby wywołali metodę rozszerzenia. Jest to rozsądna alternatywa dla poinstruowania ich, aby dodać
<RootComponent>
składnik w swoimMainLayout
składniku.
Kaskadowe wiele wartości
Aby kaskadowo utworzyć wiele wartości tego samego typu w ramach tego samego poddrzewa, podaj unikatowy Name ciąg dla każdego CascadingValue
składnika i odpowiadających im [CascadingParameter]
atrybutów.
W poniższym przykładzie dwa CascadingValue
składniki kaskadowo używają różnych wystąpień programu CascadingType
:
<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
W składniku potomnym parametry kaskadowe otrzymują swoje kaskadowe wartości ze składnika przodka przez :Name
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
Przekazywanie danych w hierarchii składników
Parametry kaskadowe umożliwiają również składnikom przekazywanie danych w hierarchii składników. Rozważmy poniższy przykład zestawu kart interfejsu użytkownika, w którym składnik zestawu kart utrzymuje serię poszczególnych kart.
Uwaga
W przypadku przykładów w tej sekcji przestrzeń nazw aplikacji to BlazorSample
. Podczas eksperymentowania z kodem we własnej przykładowej aplikacji zmień przestrzeń nazw na przestrzeń nazw przykładowej aplikacji.
Utwórz interfejs, który tabulatory ITab
implementują w folderze o nazwie UIInterfaces
.
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
Uwaga
Aby uzyskać więcej informacji na temat RenderFragmentprogramu , zobacz Razor.
Poniższy TabSet
składnik obsługuje zestaw kart. Składniki zestawu Tab
kart, które są tworzone w dalszej części tej sekcji, podaj elementy listy (<li>...</li>
) dla listy (<ul>...</ul>
).
Składniki podrzędne Tab
nie są jawnie przekazywane jako parametry do elementu TabSet
. Zamiast tego składniki podrzędne Tab
są częścią zawartości podrzędnej elementu TabSet
. Jednak TabSet
nadal potrzebuje odwołania do każdego składnika Tab
, aby mógł renderować nagłówki i aktywną kartę. Aby umożliwić tę koordynację bez konieczności dodatkowego kodu, składnik TabSet
może przekazać się jako kaskadowa wartość, która jest następnie pobierana przez składniki potomne 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();
}
}
}
Tab
Składniki malejące przechwytują element zawierający TabSet
jako parametr kaskadowy. Składniki Tab
dodają się do TabSet
współrzędnych i, aby ustawić aktywną kartę.
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);
}
}
ExampleTabSet
Poniższy składnik używa TabSet
składnika, który zawiera trzy Tab
składniki.
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;
}