Delen via


ASP.NET Core Blazor trapsgewijze waarden en parameters

Notitie

Dit is niet de nieuwste versie van dit artikel. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Waarschuwing

Deze versie van ASP.NET Core wordt niet meer ondersteund. Zie de .NET- en .NET Core-ondersteuningsbeleidvoor meer informatie. Zie de .NET 9-versie van dit artikelvoor de huidige release.

Belangrijk

Deze informatie heeft betrekking op een pre-releaseproduct dat aanzienlijk kan worden gewijzigd voordat het commercieel wordt uitgebracht. Microsoft geeft geen garanties, uitdrukkelijk of impliciet, met betrekking tot de informatie die hier wordt verstrekt.

Zie de .NET 9-versie van dit artikelvoor de huidige release.

In dit artikel wordt uitgelegd hoe u gegevens van een bovenliggend Razor-onderdeel kunt overdragen naar onderliggende onderdelen.

Trapsgewijze waarden en parameters bieden een handige manier om gegevens binnen een componenthiërarchie van een bovenliggend onderdeel naar een willekeurig aantal onderliggende onderdelen te laten vloeien. In tegenstelling tot -componentparameterszijn er voor trapsgewijze waarden en parameters geen kenmerktoewijzingen vereist voor elk afstammend component waarin de gegevens worden gebruikt. Met trapsgewijze waarden en parameters kunnen onderdelen ook met elkaar samenwerken in een onderdeelhiërarchie.

Notitie

De codevoorbeelden in dit artikel maken gebruik van nullable reference types (NRT's) en .NET-compiler statische analyse van null-status, die worden ondersteund in ASP.NET Core in .NET 6 of hoger. Als u ASP.NET Core 5.0 of eerder wilt instellen, verwijdert u de null-typeaanduiding (?) uit de CascadingType?, @ActiveTab?, RenderFragment?, ITab?, TabSet?en string? typen in de voorbeelden van het artikel.

Trapsgewijze waarden op rootniveau

Trapsgewijze waarden op hoofdniveau kunnen worden geregistreerd voor de hele onderdeelhiërarchie. Benoemde trapsgewijze waarden en abonnementen voor updatemeldingen worden ondersteund.

De volgende klasse wordt gebruikt in de voorbeelden van deze sectie.

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

De volgende registraties worden in het Program-bestand van de app gemaakt met AddCascadingValue:

  • Dalek met een eigenschapswaarde voor Units wordt geregistreerd als een vaste trapsgewijze waarde.
  • Een tweede Dalek registratie met een andere eigenschapswaarde voor Units heet 'AlphaGroup'.
builder.Services.AddCascadingValue(sp => new Dalek { Units = 123 });
builder.Services.AddCascadingValue("AlphaGroup", sp => new Dalek { Units = 456 });

In het volgende Daleks-onderdeel worden de trapsgewijze waarden weergegeven.

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

In het volgende voorbeeld wordt Dalek geregistreerd als een trapsgewijze waarde met behulp van CascadingValueSource<T>, waarbij <T> het type is. De vlag isFixed geeft aan of de waarde is opgelost. Als dit onwaar is, worden alle geadresseerden geabonneerd op updatemeldingen, die worden uitgegeven door NotifyChangedAsyncaan te roepen. Abonnementen maken overhead en verminderen de prestaties, dus stel isFixed in op true als de waarde niet verandert.

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

    return source;
});

Waarschuwing

Het registreren van een componenttype als een root-level trapsgewijze waarde registreert geen extra services voor het type en staat serviceactivatie in de component niet toe.

Behandel vereiste services afzonderlijk van trapsgewijze waarden en registreer ze afzonderlijk van het trapsgewijs type.

Vermijd het gebruik van AddCascadingValue om een onderdeeltype te registreren als trapsgewijze waarde. In plaats daarvan verpakt u de <Router>...</Router> in het Routes-onderdeel (Components/Routes.razor) met het onderdeel en gaat u over op wereldwijde interactieve server-side rendering (interactieve SSR). Zie de sectie van het -onderdeel voor een voorbeeld.

CascadingValue-onderdeel

Een vooroudercomponent biedt een trapsgewijze waarde met behulp van het Blazor framework CascadingValue component, dat een subboom van een componentenhiërarchie omvat en één waarde levert aan alle componenten binnen zijn subboom.

In het volgende voorbeeld ziet u de stroom van themagegevens in de componentenhiërarchie om een CSS-stijlklasse aan knoppen in onderliggende componenten toe te wijzen.

De volgende ThemeInfo C#-klasse geeft de themagegevens op.

Notitie

Voor de voorbeelden in deze sectie is de naamruimte van de app BlazorSample. Wanneer u experimenteert met de code in uw eigen voorbeeld-app, wijzigt u de naamruimte van de app in de naamruimte van uw voorbeeld-app.

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

Het volgende indelingsonderdeel geeft themainformatie (ThemeInfo) op als een trapsgewijze waarde voor alle componenten die samen het layoutlichaam van de Body eigenschap vormen. ButtonClass wordt een waarde van btn-successtoegewezen. Dit is een Bootstrap-knopstijl. Elk afstammend onderdeel in de componenthiërarchie kan de eigenschap ButtonClass gebruiken via de ThemeInfo trapsgewijze waarde.

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 Appbieden alternatieve benaderingen voor trapsgewijze waarden die breder van toepassing zijn op de app dan het inrichten ervan via één indelingsbestand:

  • Verpakt de opmaak van het Routes onderdeel in een CascadingValue-onderdeel om de gegevens op te geven als trapsgewijze waarde voor alle onderdelen van de app.

    In het volgende voorbeeld worden ThemeInfo-gegevens trapsgewijs van het Routes-onderdeel afgeleid.

    Routes.razor:

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

    Notitie

    Het verpakken van het Routes-componentexemplaar in het App-component (Components/App.razor) met een CascadingValue-component wordt niet ondersteund.

  • Geef een cascaderende waarde op op hoofdniveau als een service door de extensiemethode AddCascadingValue aan te roepen op de servicecollectiebouwer.

    In het volgende voorbeeld worden ThemeInfo-gegevens trapsgewijs afkomstig uit het Program-bestand.

    Program.cs

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

Zie de volgende secties van dit artikel voor meer informatie:

kenmerk [CascadingParameter]

Als u trapsgewijze waarden wilt gebruiken, declareren afstammende onderdelen trapsgewijze parameters met behulp van het [CascadingParameter] kenmerk. Trapsgewijze waarden zijn per typegebonden aan trapsgewijze parameters . Het trapsgewijs combineren van meerdere waarden van hetzelfde type wordt behandeld in de sectie Trapsgewijs combineren van meerdere waarden verderop in dit artikel.

Met het volgende onderdeel wordt de ThemeInfo trapsgewijze waarde gekoppeld aan een trapsgewijze parameter, eventueel met dezelfde naam van ThemeInfo. De parameter wordt gebruikt om de CSS-klasse in te stellen voor de knop 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++;
    }
}

Net als bij een normale onderdeelparameter worden onderdelen die een trapsgewijze parameter accepteren, aangepast wanneer de trapsgewijze waarde wordt gewijzigd. Als u bijvoorbeeld een ander thema-exemplaar configureert, wordt het ThemedCounter-onderdeel uit het CascadingValue-onderdeel-sectie opnieuw gerenderd.

MainLayout.razor:

<main>
    <div class="top-row px-4">
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <CascadingValue Value="theme">
        <article class="content px-4">
            @Body
        </article>
    </CascadingValue>
    <button @onclick="ChangeToDarkTheme">Dark mode</button>
</main>

@code {
    private ThemeInfo theme = new() { ButtonClass = "btn-success" };

    private void ChangeToDarkTheme()
    {
        theme = new() { ButtonClass = "btn-secondary" };
    }
}

CascadingValue<TValue>.IsFixed kan worden gebruikt om aan te geven dat een trapsgewijze parameter niet wordt gewijzigd na initialisatie.

Trapsgewijze waarden/parameters en grenzen van de rendermodus

Trapsgewijze parameters geven geen gegevens door over de grenzen van de rendermodus:

  • Interactieve sessies worden uitgevoerd in een andere context dan de pagina's die gebruikmaken van statische server-side rendering (statische SSR). Er is geen vereiste dat de server die de pagina produceert, zelfs dezelfde computer is die als host fungeert voor een latere Interactieve Server-sessie, waaronder voor WebAssembly-onderdelen waarbij de server een andere computer is dan de client. Het voordeel van statische server-side rendering (statische SSR) is het bereiken van de volledige prestaties van pure stateless HTML-rendering.

  • De status die de grens tussen statische en interactieve rendering overschrijdt, moet serialiseerbaar zijn. Onderdelen zijn willekeurige objecten die verwijzen naar een enorme keten van andere objecten, waaronder de renderer, de DI-container en elk DI-service-exemplaar. U moet ervoor zorgen dat de status expliciet vanuit statische SSR wordt geserialiseerd om deze beschikbaar te maken in volgende interactief gegenereerde onderdelen. Er worden twee benaderingen aangenomen:

    • Via het Blazor-framework worden parameters die via een statische SSR naar een interactieve renderinggrens worden doorgegeven, automatisch geserialiseerd als ze JSON-serializeerbaar zijn of als er een fout optreedt.
    • Status die is opgeslagen in PersistentComponentState wordt automatisch geserialiseerd en hersteld als deze JSON-serialiseerbaar is of als er een fout optreedt.

Trapsgewijze parameters zijn niet JSON-serializeerbaar omdat de typische gebruikspatronen voor trapsgewijze parameters enigszins lijken op DI-services. Er zijn vaak platformspecifieke varianten van trapsgewijze parameters, dus het zou niet nuttig zijn voor ontwikkelaars als het framework hen zou verhinderen om server-interactieve versies of WebAssembly-specifieke versies te hebben. Bovendien zijn veel trapsgewijze parameterwaarden in het algemeen niet serialiseerbaar, dus het zou onpraktisch zijn om bestaande apps bij te werken als u zou moeten stoppen met het gebruik van alle niet-serialiseerbare trapsgewijze parameterwaarden.

Aanbevelingen:

  • Als u status beschikbaar wilt maken voor alle interactieve onderdelen als trapsgewijze parameter, raden we u aan om trapsgewijze waarden op hoofdniveau te gebruiken. Er is een factory-patroon beschikbaar en de app kan bijgewerkte waarden verzenden na het opstarten van de app. Trapsgewijze waarden op hoofdniveau zijn beschikbaar voor alle onderdelen, inclusief interactieve onderdelen, omdat ze worden verwerkt als DI-services.

  • Voor auteurs van onderdeelbibliotheken kunt u een uitbreidingsmethode maken voor een bibliotheekgebruiker die vergelijkbaar is met de volgende:

    builder.Services.AddLibraryCascadingParameters();
    

    Instrueer ontwikkelaars om uw extensiemethode aan te roepen. Dit is een goed alternatief voor hen instrueren om een <RootComponent>-onderdeel aan hun MainLayout-onderdeel toe te voegen.

Meerdere waarden in cascade

Als u meerdere waarden van hetzelfde type in dezelfde substructuur wilt trapsgewijs toepassen, geeft u een unieke Name tekenreeks op voor elk CascadingValue onderdeel en de bijbehorende [CascadingParameter] kenmerken.

In het volgende voorbeeld worden twee CascadingValue onderdelen trapsgewijs verschillende exemplaren van CascadingType:

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

@code {
    private CascadingType? parentCascadeParameter1;

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

In een afstammelingsonderdeel ontvangen de trapsgewijze parameters hun trapsgewijze waarden van het bovenliggende onderdeel door Name:

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

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

Gegevens doorgeven in een onderdeelhiërarchie

Met trapsgewijze parameters kunnen onderdelen ook gegevens doorgeven in een onderdeelhiërarchie. Bekijk het volgende voorbeeld van een gebruikersinterfacetabset, waarbij een tabbladensetonderdeel een reeks afzonderlijke tabbladen onderhoudt.

Notitie

Voor de voorbeelden in deze sectie is de naamruimte van de app BlazorSample. Wanneer u experimenteert met de code in uw voorbeeld-app, wijzigt u de namespace in de namespace van uw voorbeeld-app.

Maak een ITab-interface die tabbladen implementeren in een map met de naam UIInterfaces.

UIInterfaces/ITab.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample.UIInterfaces;

public interface ITab
{
    RenderFragment ChildContent { get; }
}

Notitie

Zie ASP.NET Core Razor-onderdelenvoor meer informatie over RenderFragment.

Het volgende TabSet onderdeel onderhoudt een set tabbladen. De Tab onderdelen van de tabset, die verderop in deze sectie worden gemaakt, leveren de lijstitems (<li>...</li>) op voor de lijst (<ul>...</ul>).

Subcomponenten van Tab worden niet expliciet als parameters aan de TabSetdoorgegeven. In plaats daarvan maken de Tab-componenten deel uit van de inhoud van de TabSet. De TabSet heeft echter nog steeds een verwijzing nodig naar elk Tab onderdeel, zodat de headers en het actieve tabblad kunnen worden weergegeven. Om deze coördinatie mogelijk te maken zonder dat extra code is vereist, kan het TabSet onderdeel zichzelf leveren als een trapsgewijze waarde die vervolgens wordt opgehaald door de afstammende Tab onderdelen.

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

Afgeleide Tab-onderdelen nemen de omvattende TabSet op als een trapsgewijze parameter. De Tab onderdelen voegen zichzelf toe aan de TabSet en coördineren om het actieve tabblad in te stellen.

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

Het volgende ExampleTabSet onderdeel maakt gebruik van het TabSet-onderdeel, dat drie Tab onderdelen bevat.

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

Aanvullende informatiebronnen