ASP.NET Core Blazor の値とパラメーターのカスケード
この記事では、先祖の Razor コンポーネントから子孫のコンポーネントにデータをフローさせる方法について説明します。
"カスケード値とパラメーター" の使用は、コンポーネント階層で先祖コンポーネントから下位の任意の数の子孫コンポーネントにデータをフローさせる便利な方法です。 カスケード値およびパラメーターでは、コンポーネント パラメーターとは異なり、データが使用される各子孫コンポーネントに属性を割り当てる必要がありません。 また、カスケード値とパラメーターを使用すると、コンポーネント階層全体でコンポーネントを相互連携させることができます。
CascadingValue
コンポーネント
先祖コンポーネントは、コンポーネント階層のサブツリーをラップし、そのサブツリー内のすべてのコンポーネントに単一の値を提供する Blazor フレームワークの CascadingValue
コンポーネントを使用して、カスケード値を提供します。
次の例では、子コンポーネントのボタンに CSS 形式のクラスを提供する、レイアウト コンポーネントのコンポーネント階層におけるテーマ情報のフローを示しています。
次の ThemeInfo
C# クラスは、UIThemeClasses
という名前のフォルダーに配置され、テーマ情報を指定します。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、アプリの名前空間をお使いのサンプル アプリの名前空間に変更します。
UIThemeClasses/ThemeInfo.cs
:
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
次のレイアウト コンポーネントは、Body プロパティのレイアウト本体を構成するすべてのコンポーネントに、テーマ情報 (ThemeInfo
) をカスケード値として指定しています。 ButtonClass
には、Bootstrap ボタン形式の btn-success
値が割り当てられています。 ButtonClass
プロパティは、ThemeInfo
カスケード値を介し、コンポーネント階層内のすべての子孫コンポーネントで使用できます。
Shared/MainLayout.razor
:
@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" };
}
[CascadingParameter]
属性
子孫コンポーネントでは、[CascadingParameter]
属性を使用してカスケード型パラメーターを宣言し、カスケード値を使用します。 カスケード値は、型でカスケード型パラメーターにバインドされます。 同じ型の複数の値のカスケードについては、後でこの記事の「複数の値のカスケード」セクションで説明します。
次のコンポーネントは、オプションで同じ ThemeInfo
名を使用してカスケード型パラメーターに ThemeInfo
カスケード値をバインドします。 このパラメーターは、 Increment Counter (Themed)
ボタンの CSS クラスを設定するのに使用されます。
Pages/ThemedCounter.razor
:
@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++;
}
}
通常のコンポーネント パラメーターと同じく、カスケード型パラメーターを受け取るコンポーネントはカスケード値が変更されたときに再レンダリングされます。 たとえば、別のテーマ インスタンスを構成すると、CascadingValue
コンポーネント セクションからの ThemedCounter
コンポーネントが再レンダリングされます。
Shared/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-darkmode-success" };
}
}
CascadingValue<TValue>.IsFixed を使用すると、初期化後にカスケード型パラメーターが変更されないことを示すことができます。
複数の値のカスケード
同じサブツリー内で同じ型の値を複数カスケードするには、各 CascadingValue
コンポーネントとそれに対応する [CascadingParameter]
属性に一意の Name 文字列を指定します。
次の例では、2 つの CascadingValue
コンポーネントが、CascadingType
の異なるインスタンスをカスケードしています。
<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
子孫コンポーネントで、カスケードされたパラメーターはそれらのカスケードされた値を、次のように Name を使用して、先祖コンポーネントから受け取ります。
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
コンポーネント階層に渡ってデータを渡す
カスケード型パラメーターにより、コンポーネントがコンポーネント階層間でデータを渡せるようにすることもできます。 タブ セット コンポーネントによって一連の個別タブが維持される、次の UI タブ セットの例を考えてみてください。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、名前空間をお使いのサンプル アプリの名前空間に変更します。
UIInterfaces
という名前のフォルダーに、タブが実装する ITab
インターフェイスを作成します。
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
注意
RenderFragment について詳しくは、ASP.NET Core Razor コンポーネントに関する記事を参照してください。
一連のタブは、次の TabSet
コンポーネントによって維持されます。 リスト (<ul>...</ul>
) のリスト項目 (<li>...</li>
) は、このセクションで後で作成するタブ セットの Tab
コンポーネントによって提供されます。
子 Tab
コンポーネントは、TabSet
にパラメーターとして明示的に渡されません。 代わりに、子 Tab
コンポーネントは、TabSet
の子コンテンツに含まれます。 ただし、ヘッダーとアクティブなタブをレンダリングできるように、TabSet
は、各 Tab
コンポーネントをまだ参照する必要があります。追加のコードを必要とせずにこの調整を可能にするために、TabSet
コンポーネントでは、それ自体をカスケード値として指定し、その後に子孫 Tab
コンポーネントによって取得できるようにします。
Shared/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
コンポーネントは、カスケード型パラメーターとして含まれる TabSet
を取得します。 Tab
コンポーネントは、アクティブなタブの設定のために自身を TabSet
と座標に追加します。
Shared/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
コンポーネントは、3 つの Tab
コンポーネントを含む TabSet
コンポーネントを使用しています。
Pages/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;
}
"カスケード値とパラメーター" の使用は、コンポーネント階層で先祖コンポーネントから下位の任意の数の子孫コンポーネントにデータをフローさせる便利な方法です。 カスケード値およびパラメーターでは、コンポーネント パラメーターとは異なり、データが使用される各子孫コンポーネントに属性を割り当てる必要がありません。 また、カスケード値とパラメーターを使用すると、コンポーネント階層全体でコンポーネントを相互連携させることができます。
CascadingValue
コンポーネント
先祖コンポーネントは、コンポーネント階層のサブツリーをラップし、そのサブツリー内のすべてのコンポーネントに単一の値を提供する Blazor フレームワークの CascadingValue
コンポーネントを使用して、カスケード値を提供します。
次の例では、子コンポーネントのボタンに CSS 形式のクラスを提供する、レイアウト コンポーネントのコンポーネント階層におけるテーマ情報のフローを示しています。
次の ThemeInfo
C# クラスは、UIThemeClasses
という名前のフォルダーに配置され、テーマ情報を指定します。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、アプリの名前空間をお使いのサンプル アプリの名前空間に変更します。
UIThemeClasses/ThemeInfo.cs
:
namespace BlazorSample.UIThemeClasses;
public class ThemeInfo
{
public string? ButtonClass { get; set; }
}
次のレイアウト コンポーネントは、Body プロパティのレイアウト本体を構成するすべてのコンポーネントに、テーマ情報 (ThemeInfo
) をカスケード値として指定しています。 ButtonClass
には、Bootstrap ボタン形式の btn-success
値が割り当てられています。 ButtonClass
プロパティは、ThemeInfo
カスケード値を介し、コンポーネント階層内のすべての子孫コンポーネントで使用できます。
Shared/MainLayout.razor
:
@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" };
}
[CascadingParameter]
属性
子孫コンポーネントでは、[CascadingParameter]
属性を使用してカスケード型パラメーターを宣言し、カスケード値を使用します。 カスケード値は、型でカスケード型パラメーターにバインドされます。 同じ型の複数の値のカスケードについては、後でこの記事の「複数の値のカスケード」セクションで説明します。
次のコンポーネントは、オプションで同じ ThemeInfo
名を使用してカスケード型パラメーターに ThemeInfo
カスケード値をバインドします。 このパラメーターは、 Increment Counter (Themed)
ボタンの CSS クラスを設定するのに使用されます。
Pages/ThemedCounter.razor
:
@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++;
}
}
通常のコンポーネント パラメーターと同じく、カスケード型パラメーターを受け取るコンポーネントはカスケード値が変更されたときに再レンダリングされます。 たとえば、別のテーマ インスタンスを構成すると、CascadingValue
コンポーネント セクションからの ThemedCounter
コンポーネントが再レンダリングされます。
Shared/MainLayout.razor
:
<main>
<CascadingValue Value="@theme">
<div class="content px-4">
@Body
</div>
</CascadingValue>
<button @onclick="ChangeToDarkTheme">Dark mode</button>
</main>
@code {
private ThemeInfo theme = new() { ButtonClass = "btn-success" };
private void ChangeToDarkTheme()
{
theme = new() { ButtonClass = "btn-darkmode-success" };
}
}
CascadingValue<TValue>.IsFixed を使用すると、初期化後にカスケード型パラメーターが変更されないことを示すことができます。
複数の値のカスケード
同じサブツリー内で同じ型の値を複数カスケードするには、各 CascadingValue
コンポーネントとそれに対応する [CascadingParameter]
属性に一意の Name 文字列を指定します。
次の例では、2 つの CascadingValue
コンポーネントが、CascadingType
の異なるインスタンスをカスケードしています。
<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType? parentCascadeParameter1;
[Parameter]
public CascadingType? ParentCascadeParameter2 { get; set; }
}
子孫コンポーネントで、カスケードされたパラメーターはそれらのカスケードされた値を、次のように Name を使用して、先祖コンポーネントから受け取ります。
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType? ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType? ChildCascadeParameter2 { get; set; }
}
コンポーネント階層に渡ってデータを渡す
カスケード型パラメーターにより、コンポーネントがコンポーネント階層間でデータを渡せるようにすることもできます。 タブ セット コンポーネントによって一連の個別タブが維持される、次の UI タブ セットの例を考えてみてください。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、名前空間をお使いのサンプル アプリの名前空間に変更します。
UIInterfaces
という名前のフォルダーに、タブが実装する ITab
インターフェイスを作成します。
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces;
public interface ITab
{
RenderFragment ChildContent { get; }
}
注意
RenderFragment について詳しくは、ASP.NET Core Razor コンポーネントに関する記事を参照してください。
一連のタブは、次の TabSet
コンポーネントによって維持されます。 リスト (<ul>...</ul>
) のリスト項目 (<li>...</li>
) は、このセクションで後で作成するタブ セットの Tab
コンポーネントによって提供されます。
子 Tab
コンポーネントは、TabSet
にパラメーターとして明示的に渡されません。 代わりに、子 Tab
コンポーネントは、TabSet
の子コンテンツに含まれます。 ただし、ヘッダーとアクティブなタブをレンダリングできるように、TabSet
は、各 Tab
コンポーネントをまだ参照する必要があります。追加のコードを必要とせずにこの調整を可能にするために、TabSet
コンポーネントでは、それ自体をカスケード値として指定し、その後に子孫 Tab
コンポーネントによって取得できるようにします。
Shared/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
コンポーネントは、カスケード型パラメーターとして含まれる TabSet
を取得します。 Tab
コンポーネントは、アクティブなタブの設定のために自身を TabSet
と座標に追加します。
Shared/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
コンポーネントは、3 つの Tab
コンポーネントを含む TabSet
コンポーネントを使用しています。
Pages/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;
}
"カスケード値とパラメーター" の使用は、コンポーネント階層で先祖コンポーネントから下位の任意の数の子孫コンポーネントにデータをフローさせる便利な方法です。 カスケード値およびパラメーターでは、コンポーネント パラメーターとは異なり、データが使用される各子孫コンポーネントに属性を割り当てる必要がありません。 また、カスケード値とパラメーターを使用すると、コンポーネント階層全体でコンポーネントを相互連携させることができます。
CascadingValue
コンポーネント
先祖コンポーネントは、コンポーネント階層のサブツリーをラップし、そのサブツリー内のすべてのコンポーネントに単一の値を提供する Blazor フレームワークの CascadingValue
コンポーネントを使用して、カスケード値を提供します。
次の例では、子コンポーネントのボタンに CSS 形式のクラスを提供する、レイアウト コンポーネントのコンポーネント階層におけるテーマ情報のフローを示しています。
次の ThemeInfo
C# クラスは、UIThemeClasses
という名前のフォルダーに配置され、テーマ情報を指定します。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、アプリの名前空間をお使いのサンプル アプリの名前空間に変更します。
UIThemeClasses/ThemeInfo.cs
:
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
次のレイアウト コンポーネントは、Body プロパティのレイアウト本体を構成するすべてのコンポーネントに、テーマ情報 (ThemeInfo
) をカスケード値として指定しています。 ButtonClass
には、Bootstrap ボタン形式の btn-success
値が割り当てられています。 ButtonClass
プロパティは、ThemeInfo
カスケード値を介し、コンポーネント階層内のすべての子孫コンポーネントで使用できます。
Shared/MainLayout.razor
:
@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" };
}
[CascadingParameter]
属性
子孫コンポーネントでは、[CascadingParameter]
属性を使用してカスケード型パラメーターを宣言し、カスケード値を使用します。 カスケード値は、型でカスケード型パラメーターにバインドされます。 同じ型の複数の値のカスケードについては、後でこの記事の「複数の値のカスケード」セクションで説明します。
次のコンポーネントは、オプションで同じ ThemeInfo
名を使用してカスケード型パラメーターに ThemeInfo
カスケード値をバインドします。 このパラメーターは、 Increment Counter (Themed)
ボタンの CSS クラスを設定するのに使用されます。
Pages/ThemedCounter.razor
:
@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++;
}
}
複数の値のカスケード
同じサブツリー内で同じ型の値を複数カスケードするには、各 CascadingValue
コンポーネントとそれに対応する [CascadingParameter]
属性に一意の Name 文字列を指定します。
次の例では、2 つの CascadingValue
コンポーネントが、CascadingType
の異なるインスタンスをカスケードしています。
<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType parentCascadeParameter1;
[Parameter]
public CascadingType ParentCascadeParameter2 { get; set; }
...
}
子孫コンポーネントで、カスケードされたパラメーターはそれらのカスケードされた値を、次のように Name を使用して、先祖コンポーネントから受け取ります。
...
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType ChildCascadeParameter2 { get; set; }
}
コンポーネント階層に渡ってデータを渡す
カスケード型パラメーターにより、コンポーネントがコンポーネント階層間でデータを渡せるようにすることもできます。 タブ セット コンポーネントによって一連の個別タブが維持される、次の UI タブ セットの例を考えてみてください。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、名前空間をお使いのサンプル アプリの名前空間に変更します。
UIInterfaces
という名前のフォルダーに、タブが実装する ITab
インターフェイスを作成します。
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces
{
public interface ITab
{
RenderFragment ChildContent { get; }
}
}
注意
RenderFragment について詳しくは、ASP.NET Core Razor コンポーネントに関する記事を参照してください。
一連のタブは、次の TabSet
コンポーネントによって維持されます。 リスト (<ul>...</ul>
) のリスト項目 (<li>...</li>
) は、このセクションで後で作成するタブ セットの Tab
コンポーネントによって提供されます。
子 Tab
コンポーネントは、TabSet
にパラメーターとして明示的に渡されません。 代わりに、子 Tab
コンポーネントは、TabSet
の子コンテンツに含まれます。 ただし、ヘッダーとアクティブなタブをレンダリングできるように、TabSet
は、各 Tab
コンポーネントをまだ参照する必要があります。追加のコードを必要とせずにこの調整を可能にするために、TabSet
コンポーネントでは、それ自体をカスケード値として指定し、その後に子孫 Tab
コンポーネントによって取得できるようにします。
Shared/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 == null)
{
SetActiveTab(tab);
}
}
public void SetActiveTab(ITab tab)
{
if (ActiveTab != tab)
{
ActiveTab = tab;
StateHasChanged();
}
}
}
子孫 Tab
コンポーネントは、カスケード型パラメーターとして含まれる TabSet
を取得します。 Tab
コンポーネントは、アクティブなタブの設定のために自身を TabSet
と座標に追加します。
Shared/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
コンポーネントは、3 つの Tab
コンポーネントを含む TabSet
コンポーネントを使用しています。
Pages/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;
}
"カスケード値とパラメーター" の使用は、コンポーネント階層で先祖コンポーネントから下位の任意の数の子孫コンポーネントにデータをフローさせる便利な方法です。 カスケード値およびパラメーターでは、コンポーネント パラメーターとは異なり、データが使用される各子孫コンポーネントに属性を割り当てる必要がありません。 また、カスケード値とパラメーターを使用すると、コンポーネント階層全体でコンポーネントを相互連携させることができます。
CascadingValue
コンポーネント
先祖コンポーネントは、コンポーネント階層のサブツリーをラップし、そのサブツリー内のすべてのコンポーネントに単一の値を提供する Blazor フレームワークの CascadingValue
コンポーネントを使用して、カスケード値を提供します。
次の例では、子コンポーネントのボタンに CSS 形式のクラスを提供する、レイアウト コンポーネントのコンポーネント階層におけるテーマ情報のフローを示しています。
次の ThemeInfo
C# クラスは、UIThemeClasses
という名前のフォルダーに配置され、テーマ情報を指定します。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、アプリの名前空間をお使いのサンプル アプリの名前空間に変更します。
UIThemeClasses/ThemeInfo.cs
:
namespace BlazorSample.UIThemeClasses
{
public class ThemeInfo
{
public string ButtonClass { get; set; }
}
}
次のレイアウト コンポーネントは、Body プロパティのレイアウト本体を構成するすべてのコンポーネントに、テーマ情報 (ThemeInfo
) をカスケード値として指定しています。 ButtonClass
には、Bootstrap ボタン形式の btn-success
値が割り当てられています。 ButtonClass
プロパティは、ThemeInfo
カスケード値を介し、コンポーネント階層内のすべての子孫コンポーネントで使用できます。
Shared/MainLayout.razor
:
@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" };
}
[CascadingParameter]
属性
子孫コンポーネントでは、[CascadingParameter]
属性を使用してカスケード型パラメーターを宣言し、カスケード値を使用します。 カスケード値は、型でカスケード型パラメーターにバインドされます。 同じ型の複数の値のカスケードについては、後でこの記事の「複数の値のカスケード」セクションで説明します。
次のコンポーネントは、オプションで同じ ThemeInfo
名を使用してカスケード型パラメーターに ThemeInfo
カスケード値をバインドします。 このパラメーターは、 Increment Counter (Themed)
ボタンの CSS クラスを設定するのに使用されます。
Pages/ThemedCounter.razor
:
@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++;
}
}
複数の値のカスケード
同じサブツリー内で同じ型の値を複数カスケードするには、各 CascadingValue
コンポーネントとそれに対応する [CascadingParameter]
属性に一意の Name 文字列を指定します。
次の例では、2 つの CascadingValue
コンポーネントが、CascadingType
の異なるインスタンスをカスケードしています。
<CascadingValue Value="@parentCascadeParameter1" Name="CascadeParam1">
<CascadingValue Value="@ParentCascadeParameter2" Name="CascadeParam2">
...
</CascadingValue>
</CascadingValue>
@code {
private CascadingType parentCascadeParameter1;
[Parameter]
public CascadingType ParentCascadeParameter2 { get; set; }
...
}
子孫コンポーネントで、カスケードされたパラメーターはそれらのカスケードされた値を、次のように Name を使用して、先祖コンポーネントから受け取ります。
...
@code {
[CascadingParameter(Name = "CascadeParam1")]
protected CascadingType ChildCascadeParameter1 { get; set; }
[CascadingParameter(Name = "CascadeParam2")]
protected CascadingType ChildCascadeParameter2 { get; set; }
}
コンポーネント階層に渡ってデータを渡す
カスケード型パラメーターにより、コンポーネントがコンポーネント階層間でデータを渡せるようにすることもできます。 タブ セット コンポーネントによって一連の個別タブが維持される、次の UI タブ セットの例を考えてみてください。
Note
このセクションの例では、アプリの名前空間は BlazorSample
です。 自分独自のサンプル アプリでコードを試す場合は、名前空間をお使いのサンプル アプリの名前空間に変更します。
UIInterfaces
という名前のフォルダーに、タブが実装する ITab
インターフェイスを作成します。
UIInterfaces/ITab.cs
:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.UIInterfaces
{
public interface ITab
{
RenderFragment ChildContent { get; }
}
}
注意
RenderFragment について詳しくは、ASP.NET Core Razor コンポーネントに関する記事を参照してください。
一連のタブは、次の TabSet
コンポーネントによって維持されます。 リスト (<ul>...</ul>
) のリスト項目 (<li>...</li>
) は、このセクションで後で作成するタブ セットの Tab
コンポーネントによって提供されます。
子 Tab
コンポーネントは、TabSet
にパラメーターとして明示的に渡されません。 代わりに、子 Tab
コンポーネントは、TabSet
の子コンテンツに含まれます。 ただし、ヘッダーとアクティブなタブをレンダリングできるように、TabSet
は、各 Tab
コンポーネントをまだ参照する必要があります。追加のコードを必要とせずにこの調整を可能にするために、TabSet
コンポーネントでは、それ自体をカスケード値として指定し、その後に子孫 Tab
コンポーネントによって取得できるようにします。
Shared/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 == null)
{
SetActiveTab(tab);
}
}
public void SetActiveTab(ITab tab)
{
if (ActiveTab != tab)
{
ActiveTab = tab;
StateHasChanged();
}
}
}
子孫 Tab
コンポーネントは、カスケード型パラメーターとして含まれる TabSet
を取得します。 Tab
コンポーネントは、アクティブなタブの設定のために自身を TabSet
と座標に追加します。
Shared/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
コンポーネントは、3 つの Tab
コンポーネントを含む TabSet
コンポーネントを使用しています。
Pages/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;
}