ASP.NET Core Blazor レイアウト

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、Blazor アプリの再利用可能レイアウト コンポーネントの作成方法について説明します。

Blazor レイアウトの有用性

メニュー、著作権メッセージ、会社のロゴなどの一部のアプリ要素は、通常、アプリの全体的なプレゼンテーションの一部です。 これらの要素のマークアップのコピーをアプリのすべてのコンポーネントに配置するのは、効率的ではありません。 これらの要素のいずれかが更新されるたびに、その要素が使用されているすべてのコンポーネントを更新する必要があります。 この方法は維持するのにコストがかかり、更新が行われなかった場合にコンテンツの一貫性が失われるおそれがあります。 "レイアウト" を使用することで、これらの問題が解決されます。

Blazor レイアウトとは、それを参照するコンポーネントとマークアップを共有する Razor コンポーネントのことです。 レイアウトでは、データ バインディング依存関係の挿入、およびコンポーネントのその他の機能を使用できます。

レイアウト コンポーネント

レイアウト コンポーネントを作成する

レイアウト コンポーネントを作成するには:

  • Razor テンプレートまたは C# コードによって定義された Razor コンポーネントを作成します。 Razor テンプレートが基になっているレイアウト コンポーネントでは、通常の Razor コンポーネントと同じように .razor ファイル拡張子が使用されます。 レイアウト コンポーネントはアプリのコンポーネント間で共有されるため、通常はアプリの Shared または Layout フォルダーに配置されます。 ただし、レイアウトは、それを使用するコンポーネントにアクセスできる任意の場所に配置できます。 たとえば、それを使用するコンポーネントと同じフォルダーに、レイアウトを配置できます。
  • コンポーネントを LayoutComponentBase から継承します。 LayoutComponentBase によって、レイアウト内にレンダリングされるコンテンツの Body プロパティ (RenderFragment 型) が定義されています。
  • Razor 構文 @Body を使用して、コンテンツがレンダリングされるレイアウト マークアップ内の場所を指定します。

注意

RenderFragment の詳細については、ASP.NET Core Razor コンポーネントに関する記事を参照してください。

次の DoctorWhoLayout コンポーネントには、レイアウト コンポーネント Razor テンプレートが示されています。 レイアウトにより LayoutComponentBase が継承されて、ナビゲーション バー (<nav>...</nav>) とフッター (<footer>...</footer>) の間に @Body が設定されます。

DoctorWhoLayout.razor:

@inherits LayoutComponentBase

<PageTitle>Doctor Who® Database</PageTitle>

<header>
    <h1>Doctor Who® Database</h1>
</header>

<nav>
    <a href="main-list">Main Episode List</a>
    <a href="search">Search</a>
    <a href="new">Add Episode</a>
</nav>

@Body

<footer>
    @TrademarkMessage
</footer>

@code {
    public string TrademarkMessage { get; set; } =
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase

<header>
    <h1>Doctor Who™ Episode Database</h1>
</header>

<nav>
    <a href="main-list">Main Episode List</a>
    <a href="search">Search</a>
    <a href="new">Add Episode</a>
</nav>

@Body

<footer>
    @TrademarkMessage
</footer>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase

<header>
    <h1>Doctor Who™ Episode Database</h1>
</header>

<nav>
    <a href="main-list">Main Episode List</a>
    <a href="search">Search</a>
    <a href="new">Add Episode</a>
</nav>

@Body

<footer>
    @TrademarkMessage
</footer>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase

<header>
    <h1>Doctor Who™ Episode Database</h1>
</header>

<nav>
    <a href="main-list">Main Episode List</a>
    <a href="search">Search</a>
    <a href="new">Add Episode</a>
</nav>

@Body

<footer>
    @TrademarkMessage
</footer>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase

<header>
    <h1>Doctor Who™ Episode Database</h1>
</header>

<nav>
    <a href="main-list">Main Episode List</a>
    <a href="search">Search</a>
    <a href="new">Add Episode</a>
</nav>

@Body

<footer>
    @TrademarkMessage
</footer>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}

MainLayout コンポーネント

Blazor プロジェクト テンプレートから作成されたアプリでは、MainLayout コンポーネントがアプリの既定のレイアウトです。 Blazor のレイアウトでは Flexbox layout model (MDN documentation) (W3C 仕様) を採用しています。

Blazor の CSS 分離機能により、分離された CSS スタイルが MainLayout コンポーネントに適用されます。 慣例により、スタイルは同じ名前 MainLayout.razor.css の付随するスタイルシートによって提供されます。 スタイルシートの ASP.NET Core フレームワークの実装を、ASP.NET Core 参照ソース (dotnet/aspnetcore GitHub リポジトリ) での検査に使用できます。

Note

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

Blazor の CSS 分離機能により、分離された CSS スタイルが MainLayout コンポーネントに適用されます。 慣例により、スタイルは同じ名前 MainLayout.razor.css の付随するスタイルシートによって提供されます。 スタイルシートの ASP.NET Core フレームワークの実装を、ASP.NET Core 参照ソース (dotnet/aspnetcore GitHub リポジトリ) での検査に使用できます。

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

レイアウトを適用する

レイアウトの名前空間を使用可能にする

レイアウト ファイルの場所と名前空間は、Blazor フレームワークの時間の経過に伴って変更されてきました。 ビルドしているアプリの Blazor バージョンと Blazor の種類によっては、レイアウトの名前空間を使用するときに指定する必要がある場合があります。 レイアウトの実装を参照していて、レイアウトの名前空間の表示がなく、レイアウトが見つからない場合は、次のいずれかの方法を実行します。

  • レイアウトの場所の @using ディレクティブを _Imports.razor ファイルに追加します。 次の例では、Layout という名前が付いたレイアウトのフォルダーが Components フォルダー内にあり、アプリの名前空間は BlazorSample です。

    @using BlazorSample.Components.Layout
    
  • レイアウトが使用されるコンポーネント定義の先頭に @using ディレクティブを追加します。

    @using BlazorSample.Components.Layout
    @layout DoctorWhoLayout
    
  • レイアウトが使用される名前空間を完全修飾します。

    @layout BlazorSample.Components.Layout.DoctorWhoLayout
    

コンポーネントにレイアウトを適用する

@page ディレクティブが使用されているルーティング可能な Razor コンポーネントにレイアウトを適用するには、@layoutRazor ディレクティブを使用します。 コンパイラにより、@layoutLayoutAttribute に変換され、その属性がコンポーネント クラスに適用されます。

次の Episodes コンポーネントの内容が、@Body の位置にある DoctorWhoLayout に挿入されます。

Episodes.razor:

@page "/episodes"
@layout DoctorWhoLayout

<h2>Doctor Who® Episodes</h2>

<ul>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfknq">
            <em>The Ribos Operation</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfdsb">
            <em>The Sunmakers</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vhc26">
            <em>Nightmare of Eden</em>
        </a>
    </li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout

<h2>Episodes</h2>

<ul>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfknq">
            <em>The Ribos Operation</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfdsb">
            <em>The Sun Makers</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vhc26">
            <em>Nightmare of Eden</em>
        </a>
    </li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout

<h2>Episodes</h2>

<ul>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfknq">
            <em>The Ribos Operation</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfdsb">
            <em>The Sun Makers</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vhc26">
            <em>Nightmare of Eden</em>
        </a>
    </li>
</ul>
@page "/episodes"
@layout DoctorWhoLayout

<h2>Episodes</h2>

<ul>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfknq">
            <em>The Ribos Operation</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vfdsb">
            <em>The Sun Makers</em>
        </a>
    </li>
    <li>
        <a href="https://www.bbc.co.uk/programmes/p00vhc26">
            <em>Nightmare of Eden</em>
        </a>
    </li>
</ul>

レンダリングされた次の HTML マークアップが、前の DoctorWhoLayout および Episodes コンポーネントによって生成されます。 関連する 2 つのコンポーネントによって提供されるコンテンツに注目するため、余分なマークアップは示されていません。

  • ヘッダー (<header>...</header>) の H1 "database" という見出し (<h1>...</h1>)、ナビゲーション バー (<nav>...</nav>)、フッター (<footer>...</footer>) の商標情報は、DoctorWhoLayout コンポーネントから生成されます。
  • H2 "episodes" という見出し (<h2>...</h2>) とエピソードの一覧 (<ul>...</ul>) は、Episodes コンポーネントから生成されます。
<header>
    <h1 ...>...</h1>
</header>

<nav>
    ...
</nav>

<h2>...</h2>

<ul>
    <li>...</li>
    <li>...</li>
    <li>...</li>
</ul>

<footer>
    ...
</footer>

コンポーネントでレイアウトを直接指定すると、"既定のレイアウト" がオーバーライドされます。

レイアウトをコンポーネントのフォルダーに適用する

アプリのすべてのフォルダーには、必要に応じて、_Imports.razor という名前のテンプレート ファイルを格納できます。 コンパイラにより、インポート ファイルに指定されたディレクティブが、同じフォルダー内とそのすべてのサブフォルダー内で再帰的にすべての Razor テンプレートに含まれます。 そのため、@layout DoctorWhoLayout が含まれる _Imports.razor ファイルにより、フォルダー内のすべてのコンポーネントで DoctorWhoLayout コンポーネントが確実に使用されます。 フォルダーとサブフォルダー内のすべての Razor コンポーネント (.razor) に、@layout DoctorWhoLayout を繰り返し追加する必要はありません。

_Imports.razor:

@layout DoctorWhoLayout
...

_Imports.razor ファイルは、Razor ビューおよびページに対する _ViewImports.cshtml ファイルに似ていますが、Razor コンポーネント ファイルに限定して適用されます。

_Imports.razor でレイアウトを指定すると、ルーターの既定のアプリ レイアウトとして指定されているレイアウトがオーバーライドされます。これについては、次のセクションで説明します。

警告

Razor@layout ディレクティブをルート _Imports.razor ファイルに追加しないでください。レイアウトが無限ループになります。 既定のアプリ レイアウトを制御するには、Router コンポーネントでレイアウトを指定します。 詳細については、次の「アプリに既定のレイアウトを適用する」セクションを参照してください。

Note

@layoutRazor ディレクティブによってレイアウトが適用されるのは、@page ディレクティブが使用されているルーティング可能な Razor コンポーネントのみです。

アプリに既定のレイアウトを適用する

Router コンポーネントの RouteView コンポーネントで、既定のアプリ レイアウトを指定します。 DefaultLayout パラメータを使用してレイアウトの種類を設定します。

<RouteView RouteData="routeData" DefaultLayout="typeof({LAYOUT})" />

前の例では、{LAYOUT} プレースホルダーがレイアウトになります (例: レイアウト ファイル名が DoctorWhoLayout.razor の場合、DoctorWhoLayout)。 .NET のバージョンと Blazor アプリの種類によっては、レイアウトの名前空間を表す必要がある場合があります。 詳細については、「レイアウトの名前空間を使用可能にする」セクションを参照してください。

Router コンポーネントの RouteView で既定のレイアウトとしてレイアウトを指定することは、この記事のこれまでのセクションで説明したように、コンポーネントごとまたはフォルダーごとにレイアウトをオーバーライドできるため、便利な方法です。 レイアウトを使用する最も一般的で柔軟な方法であるため、Router コンポーネントを使用してアプリの既定のレイアウトを設定することをお勧めします。

任意のコンテンツにレイアウトを適用する (LayoutView コンポーネント)

任意の Razor テンプレート コンテンツにレイアウトを設定するには、LayoutView コンポーネントでレイアウトを指定します。 LayoutView は、任意の Razor コンポーネントで使用できます。 次の例では、MainLayout コンポーネントの NotFound テンプレート (<NotFound>...</NotFound>) に ErrorLayout という名前のレイアウト コンポーネントを設定しています。

<Router ...>
    <Found ...>
        ...
    </Found>
    <NotFound>
        <LayoutView Layout="typeof(ErrorLayout)">
            <h1>Page not found</h1>
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

.NET のバージョンと Blazor アプリの種類によっては、レイアウトの名前空間を指定する必要がある場合があります。 詳細については、「レイアウトの名前空間を使用可能にする」セクションを参照してください。

重要

Blazor Web アプリでは NotFound パラメータ (<NotFound>...</NotFound> マークアップ) は使用しませんが、フレームワークの破壊的変更を回避するため、下位互換性の目的でこのパラメータがサポートされています。 サーバー側の ASP.NET Core ミドルウェア パイプラインは、サーバー上の要求を処理します。 サーバー側の手法を使用して、不適切な要求を処理してください。 詳細については、「ASP.NET Core Blazor レンダリング モード」を参照してください。

Note

ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router コンポーネントに @trueに設定された PreferExactMatches パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。

入れ子になったレイアウト

コンポーネントであるレイアウトを参照し、そこからさらに別のレイアウトを参照することができます。 たとえば、複数レベルのメニュー構造を作成するために、入れ子になったレイアウトを使用します。

次の例に、入れ子になったレイアウトの使用方法を示しています。 「コンポーネントにレイアウトを適用する」セクションで示されている Episodes コンポーネントは、表示するコンポーネントです。 そのコンポーネントで、DoctorWhoLayout コンポーネントが参照されています。

次の DoctorWhoLayout コンポーネントは、この記事の前の方で示した例を変更したバージョンです。 ヘッダー要素とフッター要素が削除され、レイアウトで別のレイアウト ProductionsLayout が参照されています。 Episodes コンポーネントは、DoctorWhoLayout 内の @Body が出現する場所にレンダリングされます。

DoctorWhoLayout.razor:

@inherits LayoutComponentBase
@layout ProductionsLayout

<PageTitle>Doctor Who® Database</PageTitle>

<h1>Doctor Who® Database</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

@Body

<div>
    @TrademarkMessage
</div>

@code {
    public string TrademarkMessage { get; set; } =
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/ https://www.bbc.com";
}
@inherits LayoutComponentBase
@layout ProductionsLayout

<h1>Doctor Who™ Episode Database</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

@Body

<div>
    @TrademarkMessage
</div>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout

<h1>Doctor Who™ Episode Database</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

@Body

<div>
    @TrademarkMessage
</div>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout

<h1>Doctor Who™ Episode Database</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

@Body

<div>
    @TrademarkMessage
</div>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}
@inherits LayoutComponentBase
@layout ProductionsLayout

<h1>Doctor Who™ Episode Database</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

@Body

<div>
    @TrademarkMessage
</div>

@code {
    public string TrademarkMessage { get; set; } = 
        "Doctor Who is a registered trademark of the BBC. " +
        "https://www.doctorwho.tv/";
}

ProductionsLayout コンポーネントには最上位レベルのレイアウト要素が含まれ、現在はそこにヘッダー要素 (<header>...</header>) とフッター要素 (<footer>...</footer>) が存在します。 Episodes コンポーネントが含まれる DoctorWhoLayout は、@Body が出現する場所にレンダリングされます。

ProductionsLayout.razor:

@inherits LayoutComponentBase

<header>
    <h1>Productions</h1>
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

@Body

<footer>
    Footer of Productions Layout
</footer>
@inherits LayoutComponentBase

<header>
    <h1>Productions</h1>
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

@Body

<footer>
    Footer of Productions Layout
</footer>
@inherits LayoutComponentBase

<header>
    <h1>Productions</h1>
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

@Body

<footer>
    Footer of Productions Layout
</footer>
@inherits LayoutComponentBase

<header>
    <h1>Productions</h1>
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

@Body

<footer>
    Footer of Productions Layout
</footer>
@inherits LayoutComponentBase

<header>
    <h1>Productions</h1>
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

@Body

<footer>
    Footer of Productions Layout
</footer>

レンダリングされた次の HTML マークアップが、前の入れ子になったレイアウトによって生成されます。 関連する 3 つのコンポーネントによって提供される入れ子になったコンテンツに注目するため、余分なマークアップは示されていません。

  • ヘッダー (<header>...</header>)、プロダクション ナビゲーション バー (<nav>...</nav>)、フッター (<footer>...</footer>) の各要素とその内容は、ProductionsLayout コンポーネントから生成されます。
  • H1 "database" という見出し (<h1>...</h1>)、エピソード ナビゲーション バー (<nav>...</nav>)、商標情報 (<div>...</div>) は、DoctorWhoLayout コンポーネントから生成されます。
  • H2 "episodes" という見出し (<h2>...</h2>) とエピソードの一覧 (<ul>...</ul>) は、Episodes コンポーネントから生成されます。
<header>
    ...
</header>

<nav>
    <a href="main-production-list">Main Production List</a>
    <a href="production-search">Search</a>
    <a href="new-production">Add Production</a>
</nav>

<h1>...</h1>

<nav>
    <a href="main-episode-list">Main Episode List</a>
    <a href="episode-search">Search</a>
    <a href="new-episode">Add Episode</a>
</nav>

<h2>...</h2>

<ul>
    <li>...</li>
    <li>...</li>
    <li>...</li>
</ul>

<div>
    ...
</div>

<footer>
    ...
</footer>

統合コンポーネントと Razor Pages レイアウトを共有する

ルーティング可能なコンポーネントが Razor Pages アプリに統合されている場合、コンポーネントでアプリの共有レイアウトを使用できます。 詳細については、「ASP.NET Core Razor コンポーネントを ASP.NET Core アプリに統合する」をご覧ください。

ルーティング可能なコンポーネントが Razor Pages アプリに統合されている場合、コンポーネントでアプリの共有レイアウトを使用できます。 詳細については、「ASP.NET Core Razor コンポーネントのプリレンダリングと統合を行う」を参照してください。

セクション

子 Razor コンポーネントからレイアウト内のコンテンツを制御するには、「ASP.NET Core Blazor セクション」を参照してください。

その他のリソース