Megosztás a következőn keresztül:


ASP.NET Core Blazor kaszkádolt értékek és paraméterek

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadást a .NET 9 verziójú cikkben találja meg .

Ez a cikk azt ismerteti, hogyan áramolhatnak adatok egy ős Razor összetevőből a leszármazott összetevőkbe.

kaszkádolt értékek és paraméterek kényelmes módot kínálnak arra, hogy adatokat folytassunk le az összetevők hierarchiájából az ős-összetevőktől tetszőleges számú leszármazott összetevőig. A összetevőparaméterektőleltérően a kaszkádolt értékek és paraméterek nem igényelnek attribútum-hozzárendelést minden olyan leszármazott összetevőhöz, ahol az adatokat felhasználják. A kaszkádolt értékek és paraméterek azt is lehetővé teszik, hogy az összetevők egymással összehangolják egymást egy összetevőhierarchiában.

Jegyzet

A cikkben szereplő példakódok null értékű referenciatípusokat (NRT-ket) és .NET-fordító nullállapotú statikus elemzésivezetnek be, amelyeket a .NET 6-os vagy újabb verziójú ASP.NET Core támogat. A .NET 5 vagy korábbi verziójának megcélzásakor távolítsa el a null típusú megjelölést (?) a CascadingType?cikk példáiban szereplő , @ActiveTab?, RenderFragment?, ITab?, TabSet?és string? típusokból.

Gyökérszintű kaszkád értékek

A gyökérszintű kaszkádolt értékek regisztrálhatók a teljes összetevőhierarchiában. A névvel ellátott kaszkádolt értékek és a frissítési értesítésekhez tartozó előfizetések támogatottak.

A szakasz példái a következő osztályt használják.

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; }
}

Az alkalmazás Program fájljában a következő regisztrációk a AddCascadingValuesegítségével történnek:

  • Dalek Units-hez tartozó tulajdonságértékként rögzített kaszkádos értékként van regisztrálva.
  • A Dalek számára eltérő tulajdonságértékkel rendelkező második Units regisztrációt "AlphaGroup" névvel jelölték meg.
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });

Az alábbi Daleks összetevő a kaszkádolt értékeket jeleníti meg.

Daleks.razor:

@page "/daleks"

<PageTitle>Daleks</PageTitle>

<h1>Root-level Cascading Value Example</h1>

<ul>
    <li>Dalek Units: @Dalek?.Units</li>
    <li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>

<p>
    Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
    Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>

@code {
    [CascadingParameter]
    private Dalek? Dalek { get; set; }

    [CascadingParameter(Name = "AlphaGroup")]
    public Dalek? AlphaGroupDalek { get; set; }
}
@page "/daleks"

<PageTitle>Daleks</PageTitle>

<h1>Root-level Cascading Value Example</h1>

<ul>
    <li>Dalek Units: @Dalek?.Units</li>
    <li>Alpha Group Dalek Units: @AlphaGroupDalek?.Units</li>
</ul>

<p>
    Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
    Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>

@code {
    [CascadingParameter]
    private Dalek? Dalek { get; set; }

    [CascadingParameter(Name = "AlphaGroup")]
    public Dalek? AlphaGroupDalek { get; set; }
}

Az alábbi példában a Dalek kaszkádolt értékként van regisztrálva a CascadingValueSource<T>használatával, ahol <T> a típus. A isFixed jelző jelzi, hogy az érték rögzített-e. Ha falseaz összes címzett feliratkozik a frissítési értesítésekre. Az előfizetések többletterhelést okoznak, és csökkentik a teljesítményt, ezért állítsa isFixedtrue értékre, ha az érték nem változik.

builder.Services.AddCascadingValue(sp =>
{
    var dalek = new Dalek { Units = 789 };
    var source = new CascadingValueSource<Dalek>(dalek, isFixed: false);

    return source;
});

Figyelmeztetés

Ha egy összetevőtípust gyökérszintű kaszkádolt értékként regisztrál, az nem regisztrál további szolgáltatásokat a típushoz, vagy engedélyezi a szolgáltatás aktiválását az összetevőben.

A szükséges szolgáltatásokat külön kell kezelni a kaszkádolt értékektől, és ezeket külön kell regisztrálni a kaszkádolt típustól.

Ne használjon AddCascadingValue egy összetevőtípus kaszkádolt értékként való regisztrálásához. Ehelyett csomagolja a <Router>...</Router> az Routes összetevőbe (Components/Routes.razor) az összetevővel, és fogadja el a globális interaktív kiszolgálóoldali renderelést (interaktív SSR). Lásd például a CascadingValue összetevő szakaszát.

Gyökérszintű kaszkádoló értékek értesítésekkel

A frissítési értesítések kiadására a NotifyChangedAsync hívása használható annak jelzésére, hogy egy kaszkádolt érték módosult, ezzel értesítve több Razor összetevő-előfizetőt. A statikus kiszolgálóoldali renderelést (statikus SSR) alkalmazó előfizetők nem fogadhatnak értesítéseket, ezért az előfizetőknek interaktív renderelési módot kell alkalmazniuk.

Az alábbi példában:

  • NotifyingDalek implementálja a INotifyPropertyChanged-t az ügyfelek értesítésére arról, hogy megváltozott egy tulajdonság értéke. Ha a Units tulajdonság be van állítva, a rendszer meghívja a PropertyChangedEventHandler (PropertyChanged) tulajdonságot.
  • A SetUnitsToOneThousandAsync metódust az előfizetők aktiválhatják úgy, hogy szimulált feldolgozási késleltetéssel 1000-et állítsanak Units be.

Az éles kód esetében vegye figyelembe, hogy az állapot bármilyen változása (az osztály bármely tulajdonságértékének módosítása) miatt az összes előfizetett összetevő újrarendezhető, függetlenül attól, hogy az állapot melyik részét használják. Javasoljuk, hogy hozzon létre részletes osztályokat, és külön, meghatározott előfizetésekkel kaszkádolt osztályokat hozzon létre, hogy csak az alkalmazásállapot egy adott részére előfizetett összetevőkre legyenek hatással a változások.

Jegyzet

Blazor Web App Kiszolgáló- és ügyfélprojektekből.Client álló megoldások esetében a következő NotifyingDalek.cs fájl kerül a .Client projektbe.

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;
    }
}

A következő CascadingStateServiceCollectionExtensions egy CascadingValueSource<TValue>-t hoz létre egy olyan típusból, amely implementálja a INotifyPropertyChanged-t.

Jegyzet

Blazor Web App Kiszolgáló- és ügyfélprojektekből.Client álló megoldások esetében a következő CascadingStateServiceCollectionExtensions.cs fájl kerül a .Client projektbe.

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;
        }
    }
}

A típus PropertyChangedEventHandler (HandlePropertyChanged) meghívja a CascadingValueSource<TValue>NotifyChangedAsync metódust, hogy értesítse az előfizetőket arról, hogy a kaszkádolt érték megváltozott. A Task elvetésre kerül a NotifyChangedAsync hívásakor, mert a hívás csak a szinkron környezetbe történő küldés időtartamát jelzi. A kivételeket belsőleg kezeli a rendszer azáltal, hogy elküldi őket a renderelőnek annak kontextusában, hogy melyik összetevőt dobta a frissítés fogadásakor. Ugyanúgy kezelhetők a kivételek egy CascadingValue<TValue> segítségével, amely nem kap értesítést az értesítés címzettjeiben történő kivételről. Az eseménykezelőt leválasztják a Dispose metódusban, hogy megakadályozzák a memóriaszivárgást.

A Program fájlban a NotifyingDalek átadásra kerül, hogy létrehozzon egy CascadingValueSource<TValue>-t egy kezdeti Unit értékkel, ami 888 egység.

builder.Services.AddNotifyingCascadingValue(new NotifyingDalek() { Units = 888 });

Jegyzet

A Blazor Web App kiszolgáló- és ügyfélprojektekből.Client () álló megoldások esetében a rendszer az előző kódot helyezi el az egyes projektek fájljában Program .

Az alábbi összetevő bemutatja, hogyan értesíti az előfizetőket a NotifyingDalek.Units értékének módosítása.

Daleks.razor:

<h2>Daleks component</h2>

<div>
    <b>Dalek Units:</b> @Dalek?.Units
</div>

<div>
    <label>
        <span style="font-weight:bold">New Unit Count:</span>
        <input @bind="dalekCount" />
    </label>
    <button @onclick="Update">Update</button>
</div>

<div>
    <button @onclick="SetOneThousandUnits">Set Units to 1,000</button>
</div>

<p>
    Dalek© <a href="https://www.imdb.com/name/nm0622334/">Terry Nation</a><br>
    Doctor Who© <a href="https://www.bbc.co.uk/programmes/b006q2x0">BBC</a>
</p>

@code {
    private int dalekCount;

    [CascadingParameter]
    private NotifyingDalek? Dalek { get; set; }

    private void Update()
    {
        if (Dalek is not null)
        {
            Dalek.Units = dalekCount;
            dalekCount = 0;
        }
    }

    private async Task SetOneThousandUnits()
    {
        if (Dalek is not null)
        {
            await Dalek.SetUnitsToOneThousandAsync();
        }
    }
}

Több előfizetői értesítés bemutatásához az alábbi DaleksMain összetevő három Daleks összetevőt jelenít meg. Amikor az egyik Units összetevő egységszáma (Dalek) frissül, a másik két Dalek összetevő-előfizető frissül.

DaleksMain.razor:

@page "/daleks-main"

<PageTitle>Daleks Main</PageTitle>

<h1>Daleks Main</h1>

<Daleks />

<Daleks />

<Daleks />

Navigációs hivatkozás hozzáadása a DaleksMain összetevőhöz a NavMenu.razor szakaszban.

<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>

Mivel a CascadingValueSource<TValue>példában szereplő típus (NotifyingDalek) egy osztálytípus, gyakorlatilag bármilyen állapotkezelési funkció specifikációs követelményének megfelelhet. Az előfizetések azonban többletterhelést okoznak és csökkentik a teljesítményt, ezért mérje fel ennek a megközelítésnek a teljesítményét az alkalmazásban, és hasonlítsa össze más állapotkezelési megközelítésekkel , mielőtt korlátozott feldolgozási és memóriaerőforrásokkal rendelkező éles alkalmazásban bevezeti.

Az állapot bármilyen változása (az osztály bármely tulajdonságértékváltozása) miatt az összes előfizetett összetevő újrarendezhető, függetlenül attól, hogy az állapot melyik részét használják. Ne hozzon létre egyetlen nagy osztályt, amely a teljes globális alkalmazásállapotot képviseli. Ehelyett hozzon létre részletes osztályokat, és külön kaszkádozza őket a kaszkádolt paraméterekre vonatkozó előfizetésekkel, biztosítva, hogy csak az alkalmazásállapot egy adott részére előfizetett összetevőkre legyenek hatással a változások.

CascadingValue összetevő

Az ős-összetevők kaszkádolt értéket biztosítanak a Blazor keretrendszer CascadingValue összetevőjének használatával, amely egy összetevőhierarchia egy részhalmazát burkolja, és egyetlen értéket biztosít a részhalmaz összes összetevőjének.

Az alábbi példa bemutatja a témaadatok áramlását az összetevőhierarchiában, hogy CSS stílusosztályt biztosítson a gyermekösszetevők gombjaihoz.

Az alábbi ThemeInfo C# osztály határozza meg a téma adatait.

Jegyzet

Az ebben a szakaszban szereplő példák esetében az alkalmazás névtere BlazorSample. Amikor saját mintaalkalmazásában kísérletezik a kóddal, módosítsa az alkalmazás névterét a mintaalkalmazás névterére.

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; }
    }
}

Az alábbi elrendezési összetevő a témainformációkat (ThemeInfo) kaszkádolt értékként adja meg az Body tulajdonság elrendezési törzsét alkotó összes összetevőhöz. ButtonClass btn-successértékhez van hozzárendelve, amely egy Bootstrap gombstílus. Az összetevőhierarchia bármely leszármazott összetevője használhatja a ButtonClass tulajdonságot a ThemeInfo kaszkádolt értéken keresztül.

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 Appalternatív megközelítéseket kínálnak az alkalmazásra szélesebb körben alkalmazható kaszkádolt értékekhez, mint amikor ezeket egyetlen elrendezésfájlon keresztül biztosítjuk.

  • A Routes összetevő jelölését csomagolja egy CascadingValue összetevőbe, hogy az adatokat az alkalmazás összes összetevője számára kaszkádolt értékként adhassa meg.

    Az alábbi példa a ThemeInfo komponensből kaszkádolja a Routes adatokat.

    Routes.razor:

    <CascadingValue Value="theme">
        <Router ...>
            ...
        </Router>
    </CascadingValue>
    
    @code {
        private ThemeInfo theme = new() { ButtonClass = "btn-success" };
    }
    

    Jegyzet

    A Routes összetevőpéldány App összetevőben (Components/App.razor) CascadingValue összetevővel való körbefuttatása nem támogatott.

  • Adjon meg egy legfelső szintű kaszkádolt értéket szolgáltatásként a AddCascadingValue bővítménymetódus meghívásával a szolgáltatásgyűjtemény-építőben.

    Az alábbi példa a ThemeInfo adatokat kaszkádolja a Program fájlból.

    Program.cs

    builder.Services.AddCascadingValue(sp => 
        new ThemeInfo() { ButtonClass = "btn-primary" });
    

További információkért tekintse meg a cikk következő szakaszait:

[CascadingParameter] attribútum

Kaszkádolt értékek használatához a csökkenő összetevők kaszkádolt paramétereket deklarálnak a [CascadingParameter] attribútumhasználatával. A kaszkádolt értékektípus szerint kaszkádolt paraméterekhez vannak kötve. Az azonos típusú többszörös értékek kaszkádolásáról a jelen cikk későbbi részén, a Több érték kaszkádolása című szakaszban olvashat.

A private hozzáférési módosító kaszkádolt paraméterekhez ajánlott, mert a paramétert csak az összetevő osztályán belül kell használni a legtöbb esetben. Ha alosztályozásra van szükség, használja a protected hozzáférési módosítót.

Az alábbi összetevő a ThemeInfo kaszkádolt értéket egy kaszkádolt paraméterhez köti, opcionálisan a ThemeInfoazonos nevével. A paraméter a Increment Counter (Themed) gomb CSS-osztályának beállítására szolgál.

ThemedCounter.razor:

@page "/themed-counter"

<PageTitle>Themed Counter</PageTitle>

<h1>Themed Counter Example</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button 
        class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)" 
        @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo? ThemeInfo { get; set; }

    private void IncrementCount() => currentCount++;
}
@page "/themed-counter"

<PageTitle>Themed Counter</PageTitle>

<h1>Themed Counter Example</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button 
        class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)" 
        @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo? ThemeInfo { get; set; }

    private void IncrementCount() => currentCount++;
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button 
        class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)" 
        @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo? ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button 
        class="btn @(ThemeInfo is not null ? ThemeInfo.ButtonClass : string.Empty)" 
        @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo? ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}
@page "/themed-counter"
@using BlazorSample.UIThemeClasses

<h1>Themed Counter</h1>

<p>Current count: @currentCount</p>

<p>
    <button @onclick="IncrementCount">
        Increment Counter (Unthemed)
    </button>
</p>

<p>
    <button class="btn @ThemeInfo.ButtonClass" @onclick="IncrementCount">
        Increment Counter (Themed)
    </button>
</p>

@code {
    private int currentCount = 0;

    [CascadingParameter]
    private ThemeInfo ThemeInfo { get; set; }

    private void IncrementCount()
    {
        currentCount++;
    }
}

A normál összetevőparaméterhez hasonlóan a kaszkádolt paramétert elfogadó összetevők újrarendezése történik a kaszkádolt érték módosításakor. Ha például egy másik témapéldányt konfigurál, az az ThemedCounter szakaszból származó CascadingValue komponens újrarenderelését okozza.

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 arra utalhat, hogy egy kaszkádolt paraméter nem változik az inicializálás után.

Kaszkádolt értékek/paraméterek és renderelési mód határai

A kaszkádolt paraméterek nem adják át az adatokat a renderelési mód határai között:

  • Az interaktív munkamenetek más kontextusban futnak, mint a statikus kiszolgálóoldali renderelést (statikus SSR) használó lapok. Nincs szükség arra, hogy az oldalt létrehozó kiszolgáló még ugyanaz a gép is legyen, amely egy későbbi interaktív kiszolgálói munkamenetet üzemeltet, beleértve az olyan WebAssembly-összetevőket is, amelyekben a kiszolgáló egy másik gép az ügyfélhez. A statikus kiszolgálóoldali renderelés (statikus SSR) előnye a tiszta állapot nélküli HTML-renderelés teljes teljesítménye.

  • A statikus és az interaktív renderelés közötti határt átlépő állapotnak szerializálhatónak kell lennie. Az összetevők tetszőleges objektumok, amelyek más objektumok hatalmas láncára hivatkoznak, beleértve a renderelőt, a DI-tárolót és minden DI-szolgáltatáspéldányt. Az állapotnak a statikus SSR-ből történő szerializálását explicit módon kell végrehajtania, hogy az elérhetővé váljon a később interaktívan renderelt összetevőkben. Két megközelítés van elfogadva:

    • A Blazor keretrendszeren keresztül a statikus SSR és az interaktív renderelési határ között átadott paraméterek automatikusan szerializálódnak, ha azok JSON-szerializálhatók, különben hiba lép fel.
    • Az állandó összetevő állapotában tárolt állapot szerializálva lesz, és automatikusan helyreáll, ha JSON-szerializálható, vagy hiba történik.

A kaszkádolt paraméterek nem JSON-szerializálhatók, mert a kaszkádolt paraméterek tipikus használati mintái némileg hasonlóak a DI-szolgáltatásokhoz. A kaszkádolt paramétereknek gyakran vannak platformspecifikus változatai, ezért nem lenne hasznos a fejlesztők számára, ha a keretrendszer megakadályozná, hogy a fejlesztők kiszolgálóspecifikus vagy WebAssembly-specifikus verziókkal rendelkezhessenek. Emellett sok kaszkádolt paraméterérték általában nem szerializálható, ezért nem lenne célszerű frissíteni a meglévő alkalmazásokat, ha le kellene állítania az összes nem szerializálható kaszkádolt paraméterérték használatát.

Ajánlások:

  • Ha az állapotot az összes interaktív összetevő számára elérhetővé kell tennie kaszkádolt paraméterként, javasoljuk, hogy a legfelső szintű kaszkádolt értékeket vagy a legfelső szintű kaszkádolt értékeket használja értesítésekkel. Elérhető egy gyári minta, és az alkalmazás az alkalmazás indítása után képes frissített értékeket kibocsátni. A legfelső szintű kaszkádolt értékek minden összetevő számára elérhetők, beleértve az interaktív összetevőket is, mivel azok DI-szolgáltatásként vannak feldolgozva.

  • Az összetevőkönyvtárak szerzői számára létrehozhat egy kiterjesztési metódust a könyvtárhasználók számára, hasonlóan a következőhöz:

    builder.Services.AddLibraryCascadingParameters();
    

    Utasítsa a fejlesztőket, hogy hívják meg a kiterjesztési metódust. Ez egy megbízható alternatíva ahelyett, hogy arra utasítanánk őket, hogy adjanak hozzá egy <RootComponent> összetevőt a MainLayout összetevőjükhöz.

Több érték kaszkádolt

Ha több azonos típusú értéket szeretne kaszkádolva ugyanabban a részhalmazban, adjon meg egy egyedi Name sztringet az egyes CascadingValue összetevőknek, valamint a hozzájuk tartozó [CascadingParameter] attribútumokat.

Az alábbi példában két CascadingValue összetevő különböző CascadingTypepéldányokon kaszkádol:

<CascadingValue Value="parentCascadeParameter1" Name="CascadeParam1">
    <CascadingValue Value="ParentCascadeParameter2" Name="CascadeParam2">
        ...
    </CascadingValue>
</CascadingValue>

@code {
    private CascadingType? parentCascadeParameter1;

    [Parameter]
    public CascadingType? ParentCascadeParameter2 { get; set; }
}

Egy leszármazott összetevőben a kaszkádolt paraméterek kaszkádolt értékeket kapnak az előd összetevőtől Name:

@code {
    [CascadingParameter(Name = "CascadeParam1")]
    protected CascadingType? ChildCascadeParameter1 { get; set; }

    [CascadingParameter(Name = "CascadeParam2")]
    protected CascadingType? ChildCascadeParameter2 { get; set; }
}

Adatok továbbítása egy összetevőhierarchián keresztül

A kaszkádolt paraméterek lehetővé teszik az összetevők számára, hogy adatokat adjanak át egy összetevőhierarchián. Tekintse meg a következő felhasználói felület lapkészlet-példát, ahol a lapkészlet-összetevők egy sor különálló lapot tartanak fenn.

Jegyzet

Az ebben a szakaszban szereplő példák esetében az alkalmazás névtere BlazorSample. Amikor kóddal kísérletezik a saját mintaalkalmazásában, módosítsa a névteret az ön mintaalkalmazásának névterére.

Hozzon létre egy ITab felületet, amelyet a tabulátorok implementálnak egy UIInterfacesnevű mappában.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces;

public interface ITab
{
    RenderFragment ChildContent { get; }
}

Jegyzet

További információért a RenderFragment-ról nézze meg a ASP.NET Core Razor összetevők.

A következő TabSet összetevő tart fenn egy készlet lapot. A tabulátorkészlet Tab összetevői, amelyek a szakasz későbbi részében jönnek létre, adják meg a listaelemeket (<li>...</li>) a listához (<ul>...</ul>).

A gyermek Tab összetevők nem kerülnek explicit módon átadásra paraméterként a TabSet-nek. Ehelyett a gyermek Tab összetevői a TabSetgyermektartalmának részei. A TabSet-nak azonban továbbra is szüksége van hivatkozásra minden egyes Tab összetevőre, hogy megjeleníthesse az élőfejeket és az aktív lapot. Ha ezt a koordinációt további kód megkövetelése nélkül szeretnénk engedélyezni, a TabSet összetevő kaszkádolt értékként szolgáltathat-et, amelyet a leszármazott Tab összetevők felvesznek.

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();
        }
    }
}

Az alárendelt Tab összetevők kaszkádolt paraméterként rögzítik a TabSet-et. A Tab-összetevők hozzáadódnak a TabSet-hez és koordinálnak az aktív lap beállításához.

Tab.razor:

@using BlazorSample.UIInterfaces
@implements ITab

<li>
    <a @onclick="ActivateTab" class="nav-link @TitleCssClass" role="button">
        @Title
    </a>
</li>

@code {
    [CascadingParameter]
    private TabSet? ContainerTabSet { get; set; }

    [Parameter]
    public string? Title { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    private string? TitleCssClass => 
        ContainerTabSet?.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet?.AddTab(this);
    }

    private void ActivateTab()
    {
        ContainerTabSet?.SetActiveTab(this);
    }
}

A következő ExampleTabSet összetevő a TabSet összetevőt használja, amely három Tab összetevőt tartalmaz.

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;
}

További erőforrások