Bagikan melalui


ASP.NET Nilai dan parameter berskala Core Blazor

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.

Artikel ini menjelaskan cara mengalirkan data dari komponen leluhur Razor ke komponen keturunan.

Nilai dan parameter berskala menyediakan cara mudah untuk mengalirkan data ke hierarki komponen dari komponen leluhur ke sejumlah komponen keturunan. Tidak seperti parameter Komponen, nilai dan parameter berskala tidak memerlukan penetapan atribut untuk setiap komponen turunan tempat data digunakan. Nilai dan parameter berskala juga memungkinkan komponen untuk berkoordinasi satu sama lain di seluruh hierarki komponen.

Catatan

Contoh kode dalam artikel ini mengadopsi jenis referensi nullable (NRTs) dan .NET compiler null-state static analysis, yang didukung di ASP.NET Core di .NET 6 atau yang lebih baru. Saat menargetkan .NET 5 atau yang lebih lama, hapus penunjukan jenis null (?) dari CascadingType?, @ActiveTab?, RenderFragment?, ITab?, TabSet?, dan string? dalam contoh artikel.

Nilai berskala tingkat akar

Nilai kaskading tingkat akar dapat didaftarkan untuk seluruh hierarki komponen. Nilai dan langganan berskala bernama untuk pemberitahuan pembaruan didukung.

Kelas berikut digunakan dalam contoh bagian ini.

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

Pendaftaran berikut dibuat dalam file aplikasi Program dengan AddCascadingValue:

  • Dalek dengan nilai properti untuk Units didaftarkan sebagai nilai kaskading tetap.
  • Pendaftaran kedua Dalek dengan nilai properti yang berbeda untuk Units diberi nama "AlphaGroup".
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });

Komponen berikut Daleks menampilkan nilai berskala.

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

Dalam contoh berikut, Dalek terdaftar sebagai nilai berskala menggunakan CascadingValueSource<T>, di mana <T> adalah jenisnya. Bendera isFixed menunjukkan apakah nilai diperbaiki. Jika false, semua penerima berlangganan pemberitahuan pembaruan. Langganan membuat overhead dan mengurangi performa, jadi atur isFixed ke true jika nilai tidak berubah.

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

    return source;
});

Peringatan

Mendaftarkan jenis komponen sebagai nilai kaskading tingkat akar tidak mendaftarkan layanan tambahan untuk jenis atau mengizinkan aktivasi layanan dalam komponen.

Perlakukan layanan yang diperlukan secara terpisah dari nilai kaskading, mendaftarkannya secara terpisah dari jenis berskala.

Hindari menggunakan AddCascadingValue untuk mendaftarkan jenis komponen sebagai nilai berskala. Sebagai gantinya <Router>...</Router> , bungkus dalam Routes komponen (Components/Routes.razor) dengan komponen dan adopsi penyajian sisi server interaktif global (SSR interaktif). Misalnya, lihat bagian CascadingValue komponen .

Nilai tingkat dasar berlapis dengan pemberitahuan

Panggilan NotifyChangedAsync untuk mengeluarkan pemberitahuan pembaruan dapat digunakan untuk memberi sinyal kepada beberapa Razor pelanggan komponen bahwa nilai berkala telah berubah. Pemberitahuan tidak dimungkinkan untuk pelanggan yang menggunakan server-side rendering statis (SSR), sehingga pelanggan harus menggunakan mode render interaktif.

Pada contoh berikut:

  • NotifyingDalek INotifyPropertyChanged mengimplementasikan untuk memberi tahu klien bahwa nilai properti telah berubah. Ketika properti Units diatur, maka PropertyChangedEventHandler (PropertyChanged) akan dipanggil.
  • Metode SetUnitsToOneThousandAsync ini dapat dipicu oleh pelanggan langganan untuk mengatur Units menjadi 1.000 dengan penundaan pemrosesan yang disimulasikan.

Perlu diingat untuk kode produksi aplikasi bahwa setiap perubahan dalam state (setiap perubahan nilai properti kelas) menyebabkan semua komponen yang berlangganan dirender ulang, terlepas dari bagian state mana yang mereka gunakan. Sebaiknya buat kelas terperinci, mengkaskadenya secara terpisah dengan langganan tertentu untuk memastikan bahwa hanya komponen yang berlangganan bagian spesifik dari status aplikasi yang dipengaruhi oleh perubahan.

Catatan

Untuk solusi yang Blazor Web App terdiri dari proyek server dan klien (.Client), file berikut NotifyingDalek.cs ditempatkan dalam proyek .Client.

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

Berikut CascadingStateServiceCollectionExtensions membuat CascadingValueSource<TValue> dari tipe yang mengimplementasikan INotifyPropertyChanged.

Catatan

Untuk solusi yang Blazor Web App terdiri dari proyek server dan klien (.Client), file berikut CascadingStateServiceCollectionExtensions.cs ditempatkan dalam proyek .Client.

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

Jenis PropertyChangedEventHandler (HandlePropertyChanged) memanggil metode CascadingValueSource<TValue> milik NotifyChangedAsync untuk memberi tahu pengamat bahwa nilai kaskade telah berubah. Task dibuang saat memanggil NotifyChangedAsync karena panggilan hanya mewakili durasi pengiriman ke konteks sinkron. Pengecualian ditangani secara internal dengan mengirimkannya ke perender dalam konteks komponen mana pun yang dilemparkan saat menerima pembaruan. Ini adalah cara yang sama seperti pengecualian diproses dengan CascadingValue<TValue>, yang tidak diberi tahu tentang pengecualian yang terjadi di dalam penerima notifikasi. Penangan kejadian terputus dalam metode Dispose untuk mencegah kebocoran memori.

Dalam file Program, NotifyingDalek diteruskan untuk membuat CascadingValueSource<TValue> dengan nilai awal Unit 888 unit.

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

Catatan

Untuk solusi berupa proyek server dan klien (Blazor Web App), kode yang telah disebutkan sebelumnya ditempatkan dalam file setiap proyek .Client .

Komponen berikut digunakan untuk menunjukkan bagaimana perubahan nilai NotifyingDalek.Units memberi tahu pelanggan yang berlangganan.

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

Untuk mendemonstrasikan beberapa pemberitahuan pelanggan, komponen DaleksMain di bawah ini merender tiga komponen Daleks. Ketika jumlah unit (Units) dari satu komponen Dalek diperbarui, dua komponen Dalek penyedia layanan lainnya diperbarui.

DaleksMain.razor:

@page "/daleks-main"

<PageTitle>Daleks Main</PageTitle>

<h1>Daleks Main</h1>

<Daleks />

<Daleks />

<Daleks />

Tambahkan tautan navigasi ke DaleksMain komponen di 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>

Karena jenis CascadingValueSource<TValue> dalam contoh ini (NotifyingDalek) adalah jenis kelas, Anda dapat memenuhi hampir semua persyaratan spesifikasi fitur pengelolaan status. Namun, langganan membuat overhead dan mengurangi performa, jadi tolok ukur performa pendekatan ini di aplikasi Anda dan bandingkan dengan pendekatan manajemen status lainnya sebelum mengadopsinya di aplikasi produksi dengan sumber daya pemrosesan dan memori yang dibatasi.

Setiap perubahan status (setiap perubahan nilai properti kelas) menyebabkan semua komponen yang berlangganan dirender ulang, tanpa memandang bagian mana dari status yang mereka gunakan. Hindari membuat satu kelas besar yang mewakili seluruh status aplikasi global. Sebagai gantinya, buat kelas yang lebih rinci dan urutkan secara terpisah dengan langganan khusus ke parameter berantai, memastikan bahwa hanya komponen yang berlangganan ke bagian tertentu dari status aplikasi yang terpengaruh oleh perubahan.

CascadingValue komponen

Komponen leluhur menyediakan nilai berskala menggunakan Blazor komponen kerangka kerja CascadingValue , yang membungkus subtree hierarki komponen dan memasok nilai tunggal ke semua komponen dalam subtreenya.

Contoh berikut menunjukkan alur informasi tema ke hierarki komponen untuk menyediakan kelas gaya CSS ke tombol dalam komponen turunan.

Kelas C# berikut ThemeInfo menentukan informasi tema.

Catatan

Untuk contoh di bagian ini, namespace aplikasi adalah BlazorSample. Saat bereksperimen dengan kode di aplikasi sampel Anda sendiri, ubah namespace aplikasi ke namespace aplikasi sampel Anda.

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

Komponen tata letak berikut menentukan informasi tema (ThemeInfo) sebagai nilai bertingkat untuk semua komponen yang membentuk isi Body tata letak properti. ButtonClass diberi nilai btn-success, yang merupakan gaya tombol Bootstrap. Setiap komponen turunan dalam hierarki komponen dapat menggunakan ButtonClass properti melalui nilai berskala ThemeInfo .

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 menyediakan pendekatan alternatif untuk nilai bertingkat yang berlaku lebih luas ke aplikasi daripada melengkungkannya melalui satu file tata letak:

  • Bungkus markup Routes komponen dalam CascadingValue komponen untuk menentukan data sebagai nilai berskala untuk semua komponen aplikasi.

    Contoh data kaskade ThemeInfo berikut dari Routes komponen.

    Routes.razor:

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

    Catatan

    Membungkus Routes instans komponen dalam App komponen (Components/App.razor) dengan CascadingValue komponen tidak didukung.

  • Tentukan nilai berskala tingkat akar sebagai layanan dengan memanggil AddCascadingValue metode ekstensi pada penyusun kumpulan layanan.

    Contoh data kaskade ThemeInfo berikut dari Program file.

    Program.cs

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

Untuk informasi selengkapnya, lihat bagian berikut dari artikel ini:

atribut [CascadingParameter]

Untuk menggunakan nilai kaskade, komponen descendent mendeklarasikan parameter kaskade menggunakan [CascadingParameter] atribut . Nilai berskala terikat ke parameter berskala berdasarkan jenis. Kaskade beberapa nilai dengan jenis yang sama tercakup di bagian Beberapa nilai Berjendela nanti di artikel ini.

Komponen berikut mengikat ThemeInfo nilai berskala ke parameter kaskading, secara opsional menggunakan nama yang sama dari ThemeInfo. Parameter digunakan untuk mengatur kelas CSS untuk tombol 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++;
    }
}

Mirip dengan parameter komponen reguler, komponen yang menerima parameter kaskading dirender saat nilai kaskading diubah. Misalnya, mengonfigurasi instans ThemedCounter tema yang berbeda menyebabkan komponen dari bagian CascadingValue komponen dirender ulang.

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 dapat digunakan untuk menunjukkan bahwa parameter berskala tidak berubah setelah inisialisasi.

Nilai/parameter berskala dan batas mode render

Parameter berskala tidak meneruskan data di seluruh batas mode render:

  • Sesi interaktif berjalan dalam konteks yang berbeda dari halaman yang menggunakan penyajian sisi server statis (SSR statis). Tidak ada persyaratan bahwa server yang memproduksi halaman bahkan merupakan komputer yang sama yang menghosting beberapa sesi Server Interaktif nanti, termasuk untuk komponen WebAssembly di mana server adalah komputer yang berbeda dengan klien. Manfaat penyajian sisi server statis (SSR statis) adalah untuk mendapatkan performa penuh penyajian HTML stateless murni.

  • Status yang melintasi batas antara penyajian statis dan interaktif harus dapat diserialisasikan. Komponen adalah objek arbitrer yang mereferensikan rantai besar objek lain, termasuk perender, kontainer DI, dan setiap instans layanan DI. Anda harus secara eksplisit menyebabkan status diserialisasikan dari SSR statis agar tersedia dalam komponen yang dirender secara interaktif berikutnya. Dua pendekatan diadopsi:

    • Blazor Melalui kerangka kerja, parameter yang diteruskan di seluruh SSR statis ke batas penyajian interaktif diserialisasikan secara otomatis jika dapat diserialisasikan JSON, atau kesalahan dilemparkan.
    • Status yang disimpan dalam diserialisasikan dan dipulihkan PersistentComponentState secara otomatis jika dapat diserialisasikan JSON, atau kesalahan dilemparkan.

Parameter berjenjang tidak dapat diserialisasikan JSON karena pola penggunaan umum untuk parameter berjenjang agak seperti layanan DI. Seringkali ada varian parameter kaskading khusus platform, sehingga tidak akan membantu pengembang jika kerangka kerja menghentikan pengembang agar tidak memiliki versi khusus interaktif server atau versi khusus WebAssembly. Selain itu, banyak nilai parameter berjenjang secara umum tidak dapat diserialisasikan, sehingga tidak praktis untuk memperbarui aplikasi yang ada jika Anda harus berhenti menggunakan semua nilai parameter berjenjang yang tidak dapat diserialisasi.

Rekomendasi:

  • Jika Anda perlu membuat status tersedia untuk semua komponen interaktif sebagai parameter kaskading, sebaiknya gunakan nilai berskala tingkat akar atau nilai berskala tingkat akar dengan pemberitahuan. Pola pabrik tersedia, dan aplikasi dapat memancarkan nilai yang diperbarui setelah pengaktifan aplikasi. Nilai kaskading tingkat akar tersedia untuk semua komponen, termasuk komponen interaktif, karena diproses sebagai layanan DI.

  • Untuk penulis pustaka komponen, Anda dapat membuat metode ekstensi untuk konsumen pustaka yang mirip dengan yang berikut ini:

    builder.Services.AddLibraryCascadingParameters();
    

    Instruksikan pengembang untuk memanggil metode ekstensi Anda. Ini adalah alternatif yang baik untuk menginstruksikan mereka untuk menambahkan <RootComponent> komponen dalam komponennya MainLayout .

Kaskade beberapa nilai

Untuk menyimpan beberapa nilai dengan jenis yang sama dalam subtree yang sama, berikan string unik Name untuk setiap CascadingValue komponen dan atribut[CascadingParameter] sesuai.

Dalam contoh berikut, dua CascadingValue komponen kaskade instans yang berbeda dari CascadingType:

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

@code {
    private CascadingType? parentCascadeParameter1;

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

Dalam komponen turunan, parameter berkaskade menerima nilai kaskadenya dari komponen leluhur dengan Name:

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

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

Meneruskan data di seluruh hierarki komponen

Parameter berskala juga memungkinkan komponen untuk meneruskan data di seluruh hierarki komponen. Pertimbangkan contoh set tab UI berikut, di mana komponen kumpulan tab mempertahankan serangkaian tab individual.

Catatan

Untuk contoh di bagian ini, namespace aplikasi adalah BlazorSample. Saat bereksperimen dengan kode di aplikasi sampel Anda sendiri, ubah namespace ke namespace aplikasi sampel Anda.

Buat ITab antarmuka yang diterapkan tab dalam folder bernama UIInterfaces.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces;

public interface ITab
{
    RenderFragment ChildContent { get; }
}

Catatan

Untuk informasi selengkapnya tentang RenderFragment, lihat Razor ASP.NET Core.

Komponen berikut TabSet mempertahankan sekumpulan tab. Komponen kumpulan Tab tab, yang dibuat nanti di bagian ini, berikan item daftar (<li>...</li>) untuk daftar (<ul>...</ul>).

Komponen anak Tab tidak secara eksplisit diteruskan sebagai parameter ke TabSet. Sebaliknya, komponen anak Tab adalah bagian dari konten anak dari TabSet. Namun, TabSet masih memerlukan referensi ke setiap Tab komponen sehingga dapat merender header dan tab aktif. Untuk mengaktifkan koordinasi ini tanpa memerlukan kode tambahan, TabSet komponen dapat menyediakan dirinya sebagai nilai bertingkat yang kemudian diambil oleh komponen keturunan 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 Komponen turunan menangkap yang berisi TabSet sebagai parameter berskala. Komponen Tab menambahkan diri mereka ke TabSet dan berkoordinasi untuk mengatur tab aktif.

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

Komponen berikut ExampleTabSet menggunakan TabSet komponen , yang berisi tiga Tab komponen.

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

Sumber Daya Tambahan: