ASP.NET Core の Blazor ルーティングとナビゲーション
注意
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 7 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 7 バージョンを参照してください。
この記事では、Blazor アプリの要求ルーティングを管理する方法と、NavLink コンポーネントを使用してナビゲーション リンクを作成する方法について説明します。
この記事では、アプリ コードが実行される場所を区別するために、クライアント/クライアント側とサーバー/サーバー側という用語が使用されます。
- クライアント/クライアント側
- Blazor Web アプリの対話型のクライアント レンダリング。
Program
ファイルはクライアント プロジェクト (.Client
) のProgram.cs
です。 Blazor スクリプトの開始構成は、サーバー プロジェクのApp
コンポーネント (Components/App.razor
) にあります。@page
ディレクティブを持つルーティング可能な WebAssembly と自動レンダリング モード コンポーネントは、クライアント プロジェクトのPages
フォルダーに配置されます。 ルーティング不可能な共有コンポーネントは、.Client
プロジェクトのルート、またはコンポーネントの機能に基づいてカスタム フォルダーに配置します。 - Blazor WebAssembly アプリ。
Program
ファイルはProgram.cs
です。 Blazor スクリプトの開始構成はwwwroot/index.html
ファイルにあります。
- Blazor Web アプリの対話型のクライアント レンダリング。
- サーバー/サーバー側: Blazor Web アプリの対話型サーバー レンダリング。
Program
ファイルは、サーバー プロジェクトのProgram.cs
です。 Blazor スクリプトの開始構成はApp
コンポーネント (Components/App.razor
) にあります。Components/Pages
フォルダーに配置されるのは、@page
ディレクティブを持つルーティング可能なサーバー レンダリング モード コンポーネントのみです。 ルーティング不可能な共有コンポーネントは、サーバー プロジェクトのComponents
フォルダーに配置されます。 必要に応じて、コンポーネントの機能に基づいてカスタム フォルダーを作成します。
- クライアント/クライアント側
- ホスト型 Blazor WebAssembly アプリの
Client
プロジェクト。 - Blazor WebAssembly アプリ。
- Blazor スクリプトの開始構成は
wwwroot/index.html
ファイルにあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
- サーバー/サーバー側
- ホスト型 Blazor WebAssembly アプリの
Server
プロジェクト。 - Blazor Server アプリ。 Blazor スクリプトの開始構成は
Pages/_Host.cshtml
にあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
- クライアント/クライアント側
- ホスト型 Blazor WebAssembly アプリの
Client
プロジェクト。 - Blazor WebAssembly アプリ。
- Blazor スクリプトの開始構成は
wwwroot/index.html
ファイルにあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
- サーバー/サーバー側
- ホスト型 Blazor WebAssembly アプリの
Server
プロジェクト。 - Blazor Server アプリ。 Blazor スクリプトの開始構成は
Pages/_Layout.cshtml
にあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
- クライアント/クライアント側
- ホスト型 Blazor WebAssembly アプリの
Client
プロジェクト。 - Blazor WebAssembly アプリ。
- Blazor スクリプトの開始構成は
wwwroot/index.html
ファイルにあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
- サーバー/サーバー側
- ホスト型 Blazor WebAssembly アプリの
Server
プロジェクト。 - Blazor Server アプリ。 Blazor スクリプトの開始構成は
Pages/_Host.cshtml
にあります。 Program
ファイルはProgram.cs
です。
- ホスト型 Blazor WebAssembly アプリの
重要
この記事全体のコード例では、Navigation
で呼び出されるメソッドを示します。これはクラスとコンポーネントに挿入される NavigationManager です。
ルート テンプレート
Router コンポーネントは、Razor コンポーネントへのルーティングを可能にし、アプリの Routes
コンポーネント (Components/Routes.razor
) に配置されています。
@page
ディレクティブ を含む Razor コンポーネント (.razor
) がコンパイルされると、生成されたコンポーネント クラスに、コンポーネントのルート テンプレートを指定する RouteAttribute が指定されます。
アプリが起動すると、Router の AppAssembly
として指定されたアセンブリがスキャンされ、RouteAttribute を持つアプリのコンポーネントのルート情報が収集されます。
実行時に、RouteView コンポーネントは、
必要に応じて、@layout
ディレクティブ を使用してレイアウトを指定しないコンポーネントに対して、レイアウト クラスで DefaultLayout パラメーターを指定します。 フレームワークの Blazor プロジェクト テンプレートによって、MainLayout
コンポーネント (MainLayout.razor
) が、アプリの既定のレイアウトとして指定されます。 レイアウトについて詳しくは、「ASP.NET Core Blazor のレイアウト」をご覧ください。
コンポーネントにより、複数の @page
ディレクティブ を使用する複数のルート テンプレートがサポートされます。 次のコンポーネントの例では、/blazor-route
と /different-blazor-route
に対する要求を読み込みます。
BlazorRoute.razor
:
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Blazor routing</h1>
重要
URL が正しく解決されるように、アプリには <base>
タグを含め (<head>
コンテンツの場所)、href
属性でアプリのベース パスを指定する必要があります。 詳細については、「ASP.NET Core Blazor のホストと展開」を参照してください。
ルート テンプレートを @page
ディレクティブを使用して文字列リテラルとして指定する代わりに、定数ベースのルート テンプレートを @attribute
ディレクティブと共に指定できます。
次の例では、コンポーネント内の @page
ディレクティブが @attribute
ディレクティブと Constants.CounterRoute
の定数ベースのルート テンプレートに置き換えられます。これは、アプリ内の別の場所で "/counter
" に設定されています。
- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]
メモ
ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router
コンポーネントに @true
に設定された PreferExactMatches
パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。
ナビゲーション時に要素にフォーカスを合わせる
FocusOnNavigate コンポーネントでは、あるページから別のページに移動した後、CSS セレクターに基づいて要素に UI フォーカスを設定します。
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
Router コンポーネントが新しいページに移動すると、FocusOnNavigate コンポーネントによってページの最上位ヘッダー (<h1>
) にフォーカスが設定されます。 これは、スクリーン リーダーを使用する際にページ ナビゲーションが読み上げられるようにするための一般的な方法です。
コンテンツが見つからないときにカスタム コンテンツを提供する
"このセクションは Blazor WebAssembly アプリにのみ適用されます。 " Blazor Web アプリでは NotFound テンプレート (<NotFound>...</NotFound>
) は使用しませんが、フレームワークの破壊的変更を回避するため、下位互換性の目的でこのテンプレートがサポートされています。 Blazor Web アプリでは通常、ブラウザーの組み込みの 404 UI を表示するか、ASP.NET Core のミドルウェア経由で ASP.NET Core サーバーからカスタム 404 ページを返すことによって、不適切な URL 要求を処理します (例: UseStatusCodePagesWithRedirects
/ API ドキュメント)。
Router コンポーネントを使用すると、要求されたルートでコンテンツが見つからない場合に、アプリでカスタム コンテンツを指定できます。
Router コンポーネントの NotFound テンプレートにカスタム コンテンツを設定します。
<Router ...>
...
<NotFound>
...
</NotFound>
</Router>
他の対話型コンポーネントなど、<NotFound>
タグのコンテンツとして任意の項目がサポートされます。 NotFound コンテンツに既定のレイアウトを適用するには、「ASP.NET Core Blazor のレイアウト」をご覧ください。
複数のアセンブリからコンポーネントにルーティングする
AdditionalAssemblies パラメーターを使用して、Router コンポーネントで、ルーティング可能なコンポーネントを検索するときに考慮する追加のアセンブリを指定します。 AppAssembly
に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。 次の例では、Component1
は、参照されているコンポーネント クラス ライブラリに定義されているルーティング可能なコンポーネントです。 次の AdditionalAssemblies の例は、Component1
のルーティング サポートの結果を示しています。
<Router
AppAssembly="@typeof(App).Assembly"
AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
@* ... Router component elements ... *@
</Router>
<Router
AppAssembly="@typeof(Program).Assembly"
AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
@* ... Router component elements ... *@
</Router>
ルート パラメーター
ルーターでルート パラメーターを使用すれば、同じ名前の対応するコンポーネント パラメーターを設定できます。 ルート パラメーター名では大文字と小文字は区別されません。 次の例では、text
パラメーターを使用して、ルート セグメントの値をコンポーネントの Text
プロパティに割り当てます。 /route-parameter-1/amazing
に対して要求が行われると、<h1>
タグのコンテンツは Blazor is amazing!
としてレンダリングされます。
RouteParameter1.razor
:
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
}
@page "/route-parameter-1/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
}
省略可能なパラメーターがサポートされています。 次の例では、省略可能なパラメーター text
を使用して、ルート セグメントの値をコンポーネントの Text
プロパティに割り当てます。 セグメントが存在しない場合、Text
の値は fantastic
に設定されます。
省略可能なパラメーターはサポートされていません。 以下の例では、2 つの @page
ディレクティブが適用されています。 1 つ目のディレクティブでは、パラメーターを指定せずにコンポーネントへの移動を許可します。 2 つ目のディレクティブでは、{text}
ルート パラメーターの値をコンポーネントの Text
プロパティに割り当てます。
RouteParameter2.razor
:
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2/{text?}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
@page "/route-parameter-2"
@page "/route-parameter-2/{text}"
<h1>Blazor is @Text!</h1>
@code {
[Parameter]
public string Text { get; set; }
protected override void OnInitialized()
{
Text = Text ?? "fantastic";
}
}
オプションのパラメーター値が異なる同じコンポーネントへのアプリの移動を許可するには、OnInitialized{Async}
ではなく、OnParametersSet
を使用します。 前の例に基づいて、ユーザーが /route-parameter-2
から /route-parameter-2/amazing
に、または /route-parameter-2/amazing
から /route-parameter-2
に移動できる必要がある場合は、OnParametersSet
を使用してください。
protected override void OnParametersSet()
{
Text = Text ?? "fantastic";
}
Note
ルート パラメーターは、クエリ文字列値では使用できません。 クエリ文字列の使用方法については、「クエリ文字列」セクションをご覧ください。
ルート制約
ルート制約は、コンポーネントへのルート セグメントに型の一致を適用します。
次の例で、User
コンポーネントへのルートは、次の場合にのみ一致します。
- 要求 URL に
Id
ルート セグメントが存在する。 Id
セグメントが整数 (int
) 型である。
User.razor
:
@page "/user/{Id:int}"
<h1>User Id: @Id</h1>
@code {
[Parameter]
public int Id { get; set; }
}
@page "/user/{Id:int}"
<h1>User Id: @Id</h1>
@code {
[Parameter]
public int Id { get; set; }
}
@page "/user/{Id:int}"
<h1>User Id: @Id</h1>
@code {
[Parameter]
public int Id { get; set; }
}
@page "/user/{Id:int}"
<h1>User Id: @Id</h1>
@code {
[Parameter]
public int Id { get; set; }
}
注意
ルート制約は、クエリ文字列値では使用できません。 クエリ文字列の使用方法については、「クエリ文字列」セクションをご覧ください。
次の表に示すルート制約を使用できます。 インバリアント カルチャと一致するルート制約については、表の下の警告で詳細をご確認ください。
制約 | 例 | 一致の例 | インバリアント カルチャ 一致 |
---|---|---|---|
bool |
{active:bool} |
true 、FALSE |
いいえ |
datetime |
{dob:datetime} |
2016-12-31 、2016-12-31 7:32pm |
はい |
decimal |
{price:decimal} |
49.99 、-1,000.01 |
はい |
double |
{weight:double} |
1.234 、-1,001.01e8 |
はい |
float |
{weight:float} |
1.234 、-1,001.01e8 |
はい |
guid |
{id:guid} |
CD2C1638-1638-72D5-1638-DEADBEEF1638 、{CD2C1638-1638-72D5-1638-DEADBEEF1638} |
いいえ |
int |
{id:int} |
123456789 、-123456789 |
はい |
long |
{ticks:long} |
123456789 、-123456789 |
はい |
警告
URL の妥当性を検証し、CLR 型 (int
や DateTime など) に変換されるルート制約では、常にインバリアント カルチャが使用されます。 これらの制約では、URL がローカライズ不可であることが前提となります。
ルート制約は、省略可能なパラメーターでも使用できます。 次の例で、Id
は必須ですが、Option
は、省略可能なブール型のルート パラメーターです。
User.razor
:
@page "/user/{Id:int}/{Option:bool?}"
<p>
Id: @Id
</p>
<p>
Option: @Option
</p>
@code {
[Parameter]
public int Id { get; set; }
[Parameter]
public bool Option { get; set; }
}
ドットを含む URL によるルーティング
"サーバー側" の既定のルート テンプレートでは、ファイルが要求されているドット (.
) が要求 URL の最後のセグメントに含まれていると想定されます。 たとえば、相対 URL /example/some.thing
は、ルーターによって some.thing
という名前のファイルに対する要求として解釈されます。 追加の構成がないと、some.thing
が @page
ディレクティブ を含むコンポーネントにルーティングすることを意図しており、some.thing
がルート パラメーター値である場合に、アプリによって "404 - 見つかりません" という応答が返されます。 ドットが含まれる 1 つまたは複数のパラメーターを指定してルートを使用するには、アプリでカスタム テンプレートを使ってルートを構成する必要があります。
URL の最後のセグメントからルート パラメーターを受け取ることができる次の Example
コンポーネントについて考えてみます。
Example.razor
:
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string? Param { get; set; }
}
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string? Param { get; set; }
}
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string Param { get; set; }
}
@page "/example"
@page "/example/{param}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string Param { get; set; }
}
param
ルート パラメーターのドットを使って要求をルーティングするサーバー側構成の場合、Program
ファイルで省略可能なパラメーターを指定してフォールバック ページ ルート テンプレートを追加します。
app.MapFallbackToPage("/example/{param?}", "/");
ホストされている Blazor WebAssembly ソリューションの Server アプリで、param
ルート パラメーターのドットを使って要求をルーティングできるようにするには、Program
ファイルで省略可能なパラメーターを指定してフォールバック ファイル ルート テンプレートを追加します。
app.MapFallbackToFile("/example/{param?}", "index.html");
param
ルート パラメーターのドットを使って要求をルーティングするように Blazor Server アプリを構成するには、Program
ファイルで省略可能なパラメーターを指定してフォールバック ページ ルート テンプレートを追加します。
app.MapFallbackToPage("/example/{param?}", "/_Host");
ホストされている Blazor WebAssembly ソリューションの Server アプリで、param
ルート パラメーターのドットを使って要求をルーティングできるようにするには、Startup.Configure
で省略可能なパラメーターを指定してフォールバック ファイル ルート テンプレートを追加します。
Startup.cs
:
endpoints.MapFallbackToFile("/example/{param?}", "index.html");
param
ルート パラメーターのドットを使って要求をルーティングするように Blazor Server アプリを構成するには、Startup.Configure
で省略可能なパラメーターを指定してフォールバック ページ ルート テンプレートを追加します。
Startup.cs
:
endpoints.MapFallbackToPage("/example/{param?}", "/_Host");
詳細については、「ASP.NET Core のルーティング」を参照してください。
キャッチオールのルート パラメーター
複数のフォルダー境界にまたがるパスをキャプチャするキャッチオール ルート パラメーターが、コンポーネントでサポートされます。
キャッチオールのルート パラメーターは次のとおりです。
- ルート セグメント名と一致する名前が付けられている。 名前付けで大文字と小文字は区別されない。
string
型。 フレームワークによって自動キャストは提供されません。- URL の末尾。
CatchAll.razor
:
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"
@code {
[Parameter]
public string PageRoute { get; set; }
}
ルート テンプレートが /catch-all/{*pageRoute}
の URL /catch-all/this/is/a/test
の場合、PageRoute
の値は this/is/a/test
に設定されます。
キャプチャされたパスのスラッシュとセグメントはデコードされます。 /catch-all/{*pageRoute}
のルート テンプレートでは、URL /catch-all/this/is/a%2Ftest%2A
から this/is/a/test*
が生成されます。
URI およびナビゲーション状態ヘルパー
C# コード内の URI とナビゲーションを管理するには、NavigationManager を使用します。 NavigationManager には、次の表に示すイベントとメソッドがあります。
メンバー | 説明 |
---|---|
Uri | 現在の絶対 URI を取得します。 |
BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。 |
NavigateTo | 指定された URI に移動します。 forceLoad が false の場合:
forceLoad が true の場合:
詳しくは、「ナビゲーションとフォーム処理の強化」セクションをご覧ください。
|
LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。 |
ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
RegisterLocationChangingHandler |
受信ナビゲーション イベントを処理するハンドラーを登録します。 NavigateTo を呼び出すと、常にハンドラーが呼び出されます。 |
GetUriWithQueryParameter | 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
メンバー | 説明 |
---|---|
Uri | 現在の絶対 URI を取得します。 |
BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。 |
NavigateTo | 指定された URI に移動します。 forceLoad が true の場合:
replace が true の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。 |
LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。 |
ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
RegisterLocationChangingHandler |
受信ナビゲーション イベントを処理するハンドラーを登録します。 NavigateTo を呼び出すと、常にハンドラーが呼び出されます。 |
GetUriWithQueryParameter | 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
メンバー | 説明 |
---|---|
Uri | 現在の絶対 URI を取得します。 |
BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。 |
NavigateTo | 指定された URI に移動します。 forceLoad が true の場合:
replace が true の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。 |
LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。 |
ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
GetUriWithQueryParameter | 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
メンバー | 説明 |
---|---|
Uri | 現在の絶対 URI を取得します。 |
BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。 |
NavigateTo | 指定された URI に移動します。 forceLoad が true の場合:
|
LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 |
ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
ナビゲーションとフォーム処理の強化
"このセクションは Blazor WebAssembly に適用されます。"
Blazor Web Apps は、ページ ナビゲーションとフォーム処理の要求に対して 2 種類のルーティングを実行できます。
- 通常のナビゲーション (ドキュメント間のナビゲーション): 要求 URL に対してページ全体の再読み込みがトリガーされます。
- 拡張ナビゲーション (同じドキュメントのナビゲーション)†: Blazor は要求をインターセプトし、代わりに
fetch
要求を実行します。 その後、Blazor は応答の内容をページの DOM にパッチします。 Blazor の強化されたナビゲーションとフォーム処理により、ページ全体の再読み込みが不要になり、ページの状態が維持されるため、ページの読み込みが速くなり、通常はページ上のユーザーのスクロール位置が失われません。
†拡張ナビゲーションは、次の場合に使用できます。
- Blazor Server スクリプト (
blazor.server.js
) または Blazor WebAssembly スクリプト (blazor.webassembly.js
) ではなく、Blazor Web App スクリプト (blazor.web.js
) が使われます。 - この機能が明示的に無効にされることはありません。
- 宛先 URL は、内部ベース URI 空間 (アプリのベース パス) 内にあります。
対話型クライアント ルーティングで拡張ナビゲーションを使用できる場合、ナビゲーションは常に対話型クライアント側ルーターを経由します。 この点は、対話型 <Router>
がサーバー側でレンダリングされた <Router>
内に入れ子にされる、一般的でないシナリオでのみ関連があります。 その場合、ナビゲーションを処理するときに対話型ルーターが優先され、拡張ナビゲーションはサーバー側のルーティング機能であるため、ナビゲーションには使われません。
NavigateTo を呼び出すとき:
forceLoad
がfalse
の場合、これが既定値です。- また、拡張ナビゲーションは現在の URL で使用でき、Blazor の拡張ナビゲーションがアクティブにされます。
- そうでない場合、Blazor は要求された URL のページ全体の再読み込みを実行します。
forceLoad
がtrue
の場合: 拡張ナビゲーションが使用可能かどうかに関係なく、Blazor は要求された URL のページ全体の再読み込みを実行します。
NavigationManager.Refresh(bool forceLoad = false)
を呼び出すことで、現在のページを更新できます。その場合、常に拡張ナビゲーションが実行されます (使用可能な場合)。 拡張ナビゲーションを使用できない場合、Blazor はページ全体の再読み込みを実行します。
Navigation.Refresh();
拡張ナビゲーションが使用可能な場合でも、true
を forceLoad
パラメーターに渡して、ページ全体の再読み込みが常に実行されるようにします。
Navigation.Refresh(true);
拡張ナビゲーションとフォーム処理を無効にするには、「ASP.NET Core Blazor の起動」をご覧ください。
ベース URI プレフィックスに対する相対 URI を生成する
ToBaseRelativePath は、アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。
次に例を示します。
try
{
baseRelativePath = Navigation.ToBaseRelativePath(inputURI);
}
catch (ArgumentException ex)
{
...
}
アプリのベース URI が https://localhost:8000
の場合、次の結果が得られます。
inputURI
でhttps://localhost:8000/segment
を渡すと、segment
のbaseRelativePath
になります。inputURI
でhttps://localhost:8000/segment1/segment2
を渡すと、segment1/segment2
のbaseRelativePath
になります。
アプリのベース URI が inputURI
のベース URI と一致しない場合は、ArgumentException がスローされます。
inputURI
で https://localhost:8001/segment
を渡すと、次の例外が発生します。
System.ArgumentException: 'The URI 'https://localhost:8001/segment' is not contained by the base URI 'https://localhost:8000/'.'
場所の変更
LocationChanged イベントの場合、LocationChangedEventArgs でナビゲーション イベントに関する次の情報が提供されます。
- Location:新しい場所の URL。
- IsNavigationIntercepted:
true
の場合、Blazor によってブラウザーからナビゲーションがインターセプトされました。false
の場合、NavigationManager.NavigateTo によってナビゲーションが発生しました。
次のコンポーネント:
- NavigateTo を使用してボタンが選択されたときに、アプリの
Counter
コンポーネント (Counter.razor
) に移動します。 - NavigationManager.LocationChanged をサブスクライブすることで、場所の変更イベントを処理します。
HandleLocationChanged
メソッドは、Dispose
がフレームワークによって呼び出されると、アンフックになります。 このメソッドをアンフックすることで、コンポーネントのガベージ コレクションが許可されます。ロガーの実装では、ボタンが選択されたときに次の情報をログに記録します。
BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:{PORT}/counter
Navigate.razor
:
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
@page "/navigate"
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate in component code example</h1>
<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
Navigate to the Counter component
</button>
@code {
private void NavigateToCounterComponent()
{
Navigation.NavigateTo("counter");
}
protected override void OnInitialized()
{
Navigation.LocationChanged += HandleLocationChanged;
}
private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
{
Logger.LogInformation("URL of new location: {Location}", e.Location);
}
public void Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}
}
コンポーネントの破棄について詳しくは、「ASP.NET Core Razor コンポーネントのライフサイクル」をご覧ください。
ナビゲーション履歴の状態
NavigationManager では、ブラウザーの History API を使用して、アプリによって行われた各場所の変更に関連付けられているナビゲーション履歴の状態を維持します。 履歴状態の維持は、外部 ID プロバイダーを使用してユーザーを認証する場合など、外部リダイレクト シナリオで特に役立ちます。 詳細については、「ナビゲーション オプション」セクションを参照してください。
ナビゲーション オプション
NavigationOptions を NavigateTo に渡して、次の動作を制御します。
- ForceLoad: クライアント側のルーティングをバイパスし、URI がクライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーにサーバーから新しいページを読み込むよう強制します。 既定値は
false
です。 - ReplaceHistoryEntry: 履歴スタックの現在のエントリを置き換えます。
false
の場合は、履歴スタックに新しいエントリを追加します。 既定値はfalse
です。 - HistoryEntryState: 履歴エントリに追加する状態を取得または設定します。
Navigation.NavigateTo("/path", new NavigationOptions
{
HistoryEntryState = "Navigation state"
});
場所の変更の処理中にターゲット履歴エントリに関連付けられている状態を取得する方法の詳細については、「場所の変更を処理/防止する」セクション参照してください。
クエリ文字列
[SupplyParameterFromQuery]
属性を使用し、コンポーネント パラメータはクエリ文字列からのものであることを指定します。
[SupplyParameterFromQuery]
属性と共に[Parameter]
属性を使用して、ルーティング可能なコンポーネントのコンポーネント パラメーターは、クエリ文字列からのものであることを指定します。
メモ
コンポーネント パラメーターでは、@page
ディレクティブを使用したルーティング可能なコンポーネントのクエリ パラメーター値のみを受け取ることができます。
トップダウンの情報フローが妨害されないようにし、フレームワークとアプリの両方でパラメーターの処理順序を明確にするために、クエリ パラメーターを直接受け取るのはルーティング可能なコンポーネントのみです。 この設計により、特定のパラメーターの処理順序を想定して記述されたアプリ コードの特定しにくいバグを回避できます。 クエリ パラメーターの値をルーティング可能でないコンポーネントに渡すためには、自由にカスタム カスケード パラメーターを定義したり、通常のコンポーネント パラメーターに直接割り当てたりできます。
クエリ文字列から提供されるコンポーネント パラメーターでは、次の型がサポートされます。
bool
、DateTime
、decimal
、double
、float
、Guid
、int
、long
、string
。- 上記の型の Null 許容バリアント。
- 上記の型の配列 (Null 許容、Null 非許容どちらでも)。
指定された型に対して、適切なカルチャに依存しない書式設定が適用されます (CultureInfo.InvariantCulture)。
コンポーネント パラメーター名とは異なるクエリ パラメーター名を使用するには、[SupplyParameterFromQuery]
属性の Name プロパティを指定します。 次の例では、コンポーネント パラメーターの C# 名は {COMPONENT PARAMETER NAME}
です。 {QUERY PARAMETER NAME}
プレースホルダーに対して異なるクエリ パラメーター名が指定されています。
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }
[Parameter]
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }
URL が /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman
である次の例の場合:
Filter
プロパティはscifi stars
に解決されます。Page
プロパティは3
に解決されます。- 配列
Stars
はstar
という名前 (Name = "star"
) のクエリ パラメーターから入力され、LeVar Burton
とGary Oldman
に解決されます。
メモ
次のルーティング可能なページ コンポーネントのクエリ文字列パラメーターは、@page
ディレクティブの必要なく、ルーティング不可能なコンポーネントでも機能します (たとえば、他のコンポーネントで使用されている、共有 Search
コンポーネント用の Search.razor
の場合)。
Search.razor
:
@page "/search"
<h1>Search Example</h1>
<p>Filter: @Filter</p>
<p>Page: @Page</p>
@if (Stars is not null)
{
<p>Stars:</p>
<ul>
@foreach (var name in Stars)
{
<li>@name</li>
}
</ul>
}
@code {
[SupplyParameterFromQuery]
public string? Filter { get; set; }
[SupplyParameterFromQuery]
public int? Page { get; set; }
[SupplyParameterFromQuery(Name = "star")]
public string[]? Stars { get; set; }
}
Search.razor
:
@page "/search"
<h1>Search Example</h1>
<p>Filter: @Filter</p>
<p>Page: @Page</p>
@if (Stars is not null)
{
<p>Stars:</p>
<ul>
@foreach (var name in Stars)
{
<li>@name</li>
}
</ul>
}
@code {
[Parameter]
[SupplyParameterFromQuery]
public string? Filter { get; set; }
[Parameter]
[SupplyParameterFromQuery]
public int? Page { get; set; }
[Parameter]
[SupplyParameterFromQuery(Name = "star")]
public string[]? Stars { get; set; }
}
NavigationManager.GetUriWithQueryParameter
を使用して、現在の URL の 1 つ以上のクエリ パラメーターを追加、変更、または削除します。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})
前の例の場合:
{NAME}
プレースホルダーでは、クエリ パラメーター名が指定されます。{VALUE}
プレースホルダーでは、サポートされている型として値が指定されます。 サポートされている型の一覧については、このセクションで後述します。- 1 つのパラメーターを含む現在の URL と等しい文字列が返されます。
- クエリ パラメーター名が現在の URL に存在しない場合に追加されます。
- クエリ パラメーターが現在の URL に存在する場合、指定された値に更新されます。
- 指定された値の型が Null 許容で、値が
null
の場合は、削除されます。
- 指定された型に対して、適切なカルチャに依存しない書式設定が適用されます (CultureInfo.InvariantCulture)。
- クエリ パラメーターの名前と値は URL エンコードされます。
- 型のインスタンスが複数ある場合、一致するクエリ パラメーター名を持つすべての値が置き換えられます。
NavigationManager.GetUriWithQueryParameters
を呼び出して、Uri から構築され、複数のパラメーターが追加、更新、または削除された URI を作成します。 各値について、フレームワークでは value?.GetType()
を使用して各クエリ パラメーターのランタイム型が決定され、適切なカルチャに依存しない書式設定が選択されます。 サポートされていない型に対しては、フレームワークによってエラーがスローされます。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameters({PARAMETERS})
{PARAMETERS}
プレースホルダーは IReadOnlyDictionary<string, object>
です。
URI 文字列を GetUriWithQueryParameters に渡して、指定された URI から、複数のパラメーターを追加、更新、または削除して、新しい URI を生成します。 各値について、フレームワークでは value?.GetType()
を使用して各クエリ パラメーターのランタイム型が決定され、適切なカルチャに依存しない書式設定が選択されます。 サポートされていない型に対しては、フレームワークによってエラーがスローされます。 サポートされている型の一覧については、このセクションで後述します。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
{URI}
プレースホルダーは、クエリ文字列を含む URI、または含まない URI です。{PARAMETERS}
プレースホルダーはIReadOnlyDictionary<string, object>
です。
サポートされている型は、ルート制約でサポートされている型と同じです。
bool
DateTime
decimal
double
float
Guid
int
long
string
サポートされる型には次のようなものがあります。
- 上記の型の Null 許容バリアント。
- 上記の型の配列 (Null 許容、Null 非許容どちらでも)。
パラメーターが存在する場合にクエリ パラメーター値を置き換える
Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
現在の URL | 生成された URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&age=42 |
scheme://host/?full%20name=Morena%20Baccarin&age=42 |
scheme://host/?fUlL%20nAmE=David%20Krumholtz&AgE=42 |
scheme://host/?full%20name=Morena%20Baccarin&AgE=42 |
scheme://host/?full%20name=Jewel%20Staite&age=42&full%20name=Summer%20Glau |
scheme://host/?full%20name=Morena%20Baccarin&age=42&full%20name=Morena%20Baccarin |
scheme://host/?full%20name=&age=42 |
scheme://host/?full%20name=Morena%20Baccarin&age=42 |
scheme://host/?full%20name= |
scheme://host/?full%20name=Morena%20Baccarin |
パラメーターが存在しない場合にクエリ パラメーターと値を追加する
Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
現在の URL | 生成された URL |
---|---|
scheme://host/?age=42 |
scheme://host/?age=42&name=Morena%20Baccarin |
scheme://host/ |
scheme://host/?name=Morena%20Baccarin |
scheme://host/? |
scheme://host/?name=Morena%20Baccarin |
パラメーター値が null
の場合にクエリ パラメーターを削除する
Navigation.GetUriWithQueryParameter("full name", (string)null)
現在の URL | 生成された URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&age=42 |
scheme://host/?age=42 |
scheme://host/?full%20name=Sally%20Smith&age=42&full%20name=Summer%20Glau |
scheme://host/?age=42 |
scheme://host/?full%20name=Sally%20Smith&age=42&FuLl%20NaMe=Summer%20Glau |
scheme://host/?age=42 |
scheme://host/?full%20name=&age=42 |
scheme://host/?age=42 |
scheme://host/?full%20name= |
scheme://host/ |
クエリ パラメーターの追加、更新、削除
次に例を示します。
name
が削除されます (存在する場合)。age
が値25
(int
) で追加されます (存在しない場合)。 存在する場合、age
は値25
に更新されます。eye color
が値green
として追加または更新されます。
Navigation.GetUriWithQueryParameters(
new Dictionary<string, object?>
{
["name"] = null,
["age"] = (int?)25,
["eye color"] = "green"
})
現在の URL | 生成された URL |
---|---|
scheme://host/?name=David%20Krumholtz&age=42 |
scheme://host/?age=25&eye%20color=green |
scheme://host/?NaMe=David%20Krumholtz&AgE=42 |
scheme://host/?age=25&eye%20color=green |
scheme://host/?name=David%20Krumholtz&age=42&keepme=true |
scheme://host/?age=25&keepme=true&eye%20color=green |
scheme://host/?age=42&eye%20color=87 |
scheme://host/?age=25&eye%20color=green |
scheme://host/? |
scheme://host/?age=25&eye%20color=green |
scheme://host/ |
scheme://host/?age=25&eye%20color=green |
列挙可能な値のサポート
次に例を示します。
full name
が単一の値Morena Baccarin
として追加または更新されます。ping
パラメーターが35
、16
、87
、240
として追加されるか置き換えられます。
Navigation.GetUriWithQueryParameters(
new Dictionary<string, object?>
{
["full name"] = "Morena Baccarin",
["ping"] = new int?[] { 35, 16, null, 87, 240 }
})
現在の URL | 生成された URL |
---|---|
scheme://host/?full%20name=David%20Krumholtz&ping=8&ping=300 |
scheme://host/?full%20name=Morena%20Baccarin&ping=35&ping=16&ping=87&ping=240 |
scheme://host/?ping=8&full%20name=David%20Krumholtz&ping=300 |
scheme://host/?ping=35&full%20name=Morena%20Baccarin&ping=16&ping=87&ping=240 |
scheme://host/?ping=8&ping=300&ping=50&ping=68&ping=42 |
scheme://host/?ping=35&ping=16&ping=87&ping=240&full%20name=Morena%20Baccarin |
追加または変更されたクエリ文字列を使用して移動する
追加または変更されたクエリ文字列を使用して移動するには、生成された URL を NavigateTo に渡します。
次の例では以下を呼び出します。
- GetUriWithQueryParameter を呼び出し、値
Morena Baccarin
を使用してname
クエリ パラメーターを追加または置き換えます。 - NavigateTo を呼び出して、新しい URL へのナビゲーションをトリガーします。
Navigation.NavigateTo(
Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));
要求のクエリ文字列は、NavigationManager.Uri プロパティから取得されます。
@inject NavigationManager Navigation
...
var query = new Uri(Navigation.Uri).Query;
クエリ文字列のパラメーターを解析するための 1 つの方法は、JavaScript (JS) 相互運用で URLSearchParams
を使用することです。
export createQueryString = (string queryString) => new URLSearchParams(queryString);
JavaScript モジュールを使用した JavaScript の分離について詳しくは、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。
名前付き要素へのハッシュされたルーティング
要素へのハッシュされた (#
) 参照による次の方法を使って、名前付き要素に移動します。 コンポーネント内の要素へのルートと、外部コンポーネント内の要素へのルートでは、ルート (root) 相対パスが使われます。 先頭のスラッシュ (/
) は省略できます。
次の各方法の例では、Counter
コンポーネント内の id
が targetElement
の要素への移動を示します。
アンカー要素 (
<a>
) とhref
:<a href="/counter#targetElement">
NavLink コンポーネントと
href
:<NavLink href="/counter#targetElement">
NavigationManager.NavigateTo に相対 URL を渡す:
Navigation.NavigateTo("/counter#targetElement");
次の例では、コンポーネント内の名前付きの H2 見出しと外部コンポーネントへの、ハッシュされたルーティングを示します。
Home
(Home.razor
) と Counter
(Counter.razor
) コンポーネントでは、ナビゲーション ターゲットとして機能するように、次のマークアップを既存のコンポーネント マークアップの末尾に配置します。 <div>
では、ブラウザーのスクロール動作を示すために、わざと縦方向のスペースを作成しています。
<div class="border border-info rounded bg-info" style="height:500px"></div>
<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
次の HashedRouting
コンポーネントをアプリに追加します。
HashedRouting.razor
:
@page "/hashed-routing"
@inject NavigationManager Navigation
<PageTitle>Hashed routing</PageTitle>
<h1>Hashed routing to named elements</h1>
<ul>
<li>
<a href="/hashed-routing#targetElement">
Anchor in this component
</a>
</li>
<li>
<a href="/#targetElement">
Anchor to the <code>Home</code> component
</a>
</li>
<li>
<a href="/counter#targetElement">
Anchor to the <code>Counter</code> component
</a>
</li>
<li>
<NavLink href="/hashed-routing#targetElement">
Use a `NavLink` component in this component
</NavLink>
</li>
<li>
<button @onclick="NavigateToElement">
Navigate with <code>NavigationManager</code> to the
<code>Counter</code> component
</button>
</li>
</ul>
<div class="border border-info rounded bg-info" style="height:500px"></div>
<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
@code {
private void NavigateToElement()
{
Navigation.NavigateTo("/counter#targetElement");
}
}
<Navigating>
コンテンツとのユーザー操作
Router コンポーネントによって、ページ切り替えの発生をユーザーに示すことができます。
Router コンポーネントを指定するコンポーネントの上部に、Microsoft.AspNetCore.Components.Routing 名前空間の @using
ディレクティブを追加します。
@using Microsoft.AspNetCore.Components.Routing
ページ切り替えイベント中に表示するマークアップを含むコンポーネントに <Navigating>
タグを追加します。 詳細については、Navigating (API ドキュメント) を参照してください。
ルーター要素コンテンツ (<Router>...</Router>
) は、次のとおりです。
<Navigating>
<p>Loading the requested page…</p>
</Navigating>
Navigating プロパティを使う例については、「ASP.NET Core Blazor WebAssembly でのアセンブリの遅延読み込み」をご覧ください。
OnNavigateAsync
で非同期ナビゲーション イベントを処理する
Router コンポーネントは、OnNavigateAsync 機能をサポートしています。 OnNavigateAsync ハンドラーは、ユーザーが次のことを行った場合に呼び出されます。
- ブラウザー内でルートに直接移動して、初めてアクセスする。
- リンクまたは NavigationManager.NavigateTo 呼び出しを使用して新しいルートに移動する。
<Router AppAssembly="@typeof(App).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
...
}
}
<Router AppAssembly="@typeof(Program).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
...
}
}
OnNavigateAsync を使う例については、「ASP.NET Core Blazor WebAssembly でのアセンブリの遅延読み込み」をご覧ください。
サーバー上でプリレンダリングする場合、OnNavigateAsync が "2 回" 実行されます。
- 1 回目は、要求されたエンドポイント コンポーネントが、最初に静的にレンダリングされるとき。
- 2 回目は、エンドポイント コンポーネントがブラウザーによってレンダリングされるとき。
OnNavigateAsync で開発者コードが 2 回実行されないようにするには、Routes
コンポーネントに、OnAfterRender{Async}
で使用する NavigationContext を格納します (firstRender
がチェックされている場合)。 詳細については、「Blazor のライフサイクル」の記事にある「JavaScript 相互運用を使用したプリレンダリング」を参照してください。
OnNavigateAsync で開発者コードが 2 回実行されないようにするには、App
コンポーネントに、OnAfterRender{Async}
で使用する NavigationContext を格納します (firstRender
がチェックされている場合)。 詳細については、「Blazor のライフサイクル」の記事にある「JavaScript 相互運用を使用したプリレンダリング」を参照してください。
OnNavigateAsync
でキャンセルを処理する
OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 このキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。
ユーザーがエンドポイントに移動したものの、その後すぐに新しいエンドポイントに移動する場合、アプリで最初のエンドポイントの OnNavigateAsync コールバックを実行し続けるべきではありません。
次に例を示します。
- キャンセル トークンは、
PostAsJsonAsync
への呼び出しで渡されます。ユーザーが/about
エンドポイントから離れた場合は、POST が取り消されます。 - ユーザーが
/store
エンドポイントから移動した場合は、製品のプリフェッチ操作中にキャンセル トークンが設定されます。
@inject HttpClient Http
@inject ProductCatalog Products
<Router AppAssembly="@typeof(App).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext context)
{
if (context.Path == "/about")
{
var stats = new Stats { Page = "/about" };
await Http.PostAsJsonAsync("api/visited", stats,
context.CancellationToken);
}
else if (context.Path == "/store")
{
var productIds = new[] { 345, 789, 135, 689 };
foreach (var productId in productIds)
{
context.CancellationToken.ThrowIfCancellationRequested();
Products.Prefetch(productId);
}
}
}
}
@inject HttpClient Http
@inject ProductCatalog Products
<Router AppAssembly="@typeof(Program).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext context)
{
if (context.Path == "/about")
{
var stats = new Stats { Page = "/about" };
await Http.PostAsJsonAsync("api/visited", stats,
context.CancellationToken);
}
else if (context.Path == "/store")
{
var productIds = new[] { 345, 789, 135, 689 };
foreach (var productId in productIds)
{
context.CancellationToken.ThrowIfCancellationRequested();
Products.Prefetch(productId);
}
}
}
}
メモ
NavigationContext 内のキャンセル トークンが取り消された場合にスローしないと、前のナビゲーションからのコンポーネントをレンダリングするなど、意図しない動作が発生する可能性があります。
場所の変更を処理/防止する
RegisterLocationChangingHandler では、受信ナビゲーション イベントを処理するハンドラーを登録します。 LocationChangingContext によって提供されるハンドラーのコンテキストには、次のプロパティが含まれます。
- TargetLocation: ターゲットの場所を取得します。
- HistoryEntryState: ターゲット履歴エントリに関連付けられている状態を取得します。
- IsNavigationIntercepted: ナビゲーションがリンクからインターセプトされたかどうかを取得します。
- CancellationToken: ナビゲーションが取り消されたかどうかを判断する CancellationToken を取得します。たとえば、ユーザーが別のナビゲーションをトリガーしたかどうかを判断します。
- PreventNavigation: ナビゲーションが続行されないようにするために呼び出されます。
コンポーネントでは、OnAfterRender
または OnAfterRenderAsync
メソッドに複数の場所変更ハンドラーを登録できます。 ナビゲーションでは、(複数のコンポーネントにまたがって) アプリ全体に登録されているすべての場所変更ハンドラーを呼び出し、すべての内部ナビゲーションでそれらをすべて並列で実行します。 NavigateTo に加え、次の場合にハンドラーが呼び出されます。
- 内部リンクを選択する場合。これは、アプリのベース パスの下にある URL を指すリンクです。
- ブラウザーの [進む] ボタンと [戻る] ボタンを使用して移動する場合。
ハンドラーは、アプリ内の内部ナビゲーションに対してのみ実行されます。 ユーザーが別のサイトに移動するリンクを選択した場合、またはアドレス バーを別のサイトに手動で変更した場合、場所変更ハンドラーは実行されません。
IDisposable を実装し、登録済みハンドラーを破棄して登録を解除します。 詳しくは、「ASP.NET Core Razor コンポーネントのライフサイクル」をご覧ください。
重要
場所の変更を処理するときは、JavaScript (JS) 相互運用機能を使って DOM のクリーンアップ タスクを実行しないでください。 クライアントの JS で MutationObserver
パターンを使います。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。
次の例では、ナビゲーション イベントの場所変更ハンドラーが登録されています。
NavHandler.razor
:
@page "/nav-handler"
@inject NavigationManager Navigation
@implements IDisposable
<p>
<button @onclick="@(() => Navigation.NavigateTo("/"))">
Home (Allowed)
</button>
<button @onclick="@(() => Navigation.NavigateTo("/counter"))">
Counter (Prevented)
</button>
</p>
@code {
private IDisposable? registration;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
registration =
Navigation.RegisterLocationChangingHandler(OnLocationChanging);
}
}
private ValueTask OnLocationChanging(LocationChangingContext context)
{
if (context.TargetLocation == "/counter")
{
context.PreventNavigation();
}
return ValueTask.CompletedTask;
}
public void Dispose() => registration?.Dispose();
}
内部ナビゲーションは非同期的に取り消すことができるため、登録されたハンドラーに対して複数の重複する呼び出しが発生する可能性があります。 たとえば、ユーザーがすぐにページの [戻る] ボタンを選択したり、ナビゲーションが実行される前に複数のリンクを選択したりすると、複数のハンドラー呼び出しが発生することがあります。 非同期ナビゲーション ロジックの概要を以下に示します。
- 場所変更ハンドラーが登録されている場合は、最初にすべてのナビゲーションが元に戻されてから、ナビゲーションが取り消されていない場合は再生されます。
- 重複するナビゲーション要求が行われた場合、最新の要求では常に以前の要求を取り消します。これは、次のことを意味します。
- アプリで、複数の [戻る] と [進む] ボタンの選択が 1 つの選択として扱われる場合があります。
- ナビゲーションが完了する前にユーザーが複数のリンクを選択した場合、最後に選択したリンクによってナビゲーションが決まります。
ナビゲーション履歴スタックのエントリと状態を制御するために NavigateTo に NavigationOptions を渡す方法の詳細については、「ナビゲーション オプション」セクションを参照してください。
その他のコード例については、「BasicTestApp
」(dotnet/aspnetcore
参照ソース) の NavigationManagerComponent
に関するページを参照してください。
注意
通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。
表示されている限り NavigationLock
コンポーネントはナビゲーション イベントをインターセプトし、続行またはキャンセルの決定が行われるまで、特定のナビゲーションを効果的に "ロック" します。 ナビゲーション インターセプトのスコープをコンポーネントの有効期間に設定できる場合、NavigationLock
を使用します。
NavigationLock パラメーター:
- ConfirmExternalNavigation では、外部ナビゲーションの確認またはキャンセルをユーザーに求めるブラウザー ダイアログを設定します。 既定値は
false
です。 確認ダイアログを表示するには、ブラウザーのアドレス バーの URL を使用して外部ナビゲーションをトリガーする前に、最初にユーザーがページを操作する必要があります。 操作の要件について詳しくは、「Window:beforeunload
イベント」(MDN ドキュメント) を参照してください。 - OnBeforeInternalNavigation では、内部ナビゲーション イベントのコールバックを設定します。
次の NavLock
コンポーネントでは、以下のことを行います。
https://www.microsoft.com
へのナビゲーションを成功させるには、Microsoft の Web サイトに従う試みをユーザーが確認する必要があります。- PreventNavigation は、JS
confirm
ダイアログを生成する JavaScript (JS) 相互運用機能呼び出しによるナビゲーションの確認をユーザーが拒否した場合に、ナビゲーションが発生しないようにするために呼び出されます。
NavLock.razor
:
@page "/nav-lock"
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation
<NavigationLock ConfirmExternalNavigation="true"
OnBeforeInternalNavigation="OnBeforeInternalNavigation" />
<p>
<button @onclick="Navigate">Navigate</button>
</p>
<p>
<a href="https://www.microsoft.com">Microsoft homepage</a>
</p>
@code {
private void Navigate()
{
Navigation.NavigateTo("/");
}
private async Task OnBeforeInternalNavigation(LocationChangingContext context)
{
var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm",
"Are you sure you want to navigate to the root page?");
if (!isConfirmed)
{
context.PreventNavigation();
}
}
}
その他のコード例については、「BasicTestApp
」(dotnet/aspnetcore
参照ソース) の ConfigurableNavigationLock
コンポーネントに関するページを参照してください。
NavLink
および NavMenu
コンポーネント
ナビゲーション リンクを作成するときは、HTML ハイパーリンク要素 (<a>
) の代わりに NavLink コンポーネントを使用します。 NavLink コンポーネントは <a>
要素のように動作しますが、href
が現在の URL と一致するかどうかに基づいて active
CSS クラスを切り替える点が異なります。 active
クラスは、表示されているナビゲーション リンクの中でどのページがアクティブ ページであるかをユーザーが理解するのに役立ちます。 必要に応じて、CSS クラス名を NavLink.ActiveClass に割り当てて、現在のルートが href
と一致したときに、レンダリングされるリンクにカスタム CSS クラスを適用します。
<NavLink>
要素の Match
属性に割り当てられる 2 つの NavLinkMatch オプションがあります。
- NavLinkMatch.All:NavLink は、現在の URL 全体に一致する場合にアクティブになります。
- NavLinkMatch.Prefix (既定値):NavLink は、現在の URL の任意のプレフィックスに一致する場合にアクティブになります。
前の例では、HomeNavLinkhref=""
はホーム URL と一致し、アプリの既定のベース パス (/
) でのみ active
CSS クラスを受け取ります。 2 番目の NavLink は、ユーザーが component
プレフィックスを含む任意の URL (/component
や /component/another-segment
など) にアクセスしたときに、active
クラスを受け取ります。
追加の NavLink コンポーネント属性は、レンダリングされるアンカー タグに渡されます。 次の例では、NavLink コンポーネントに target
属性が含まれています。
<NavLink href="example-page" target="_blank">Example page</NavLink>
次の HTML マークアップがレンダリングされます。
<a href="example-page" target="_blank">Example page</a>
警告
Blazor による子コンテンツのレンダリング方法により、for
ループ内の NavLink
コンポーネントのレンダリングでは、インクリメントするループ変数が NavLink
(子) コンポーネントのコンテンツ内で使用されている場合、ローカル インデックス変数が必要になります。
@for (int c = 0; c < 10; c++)
{
var current = c;
<li ...>
<NavLink ... href="@c">
<span ...></span> @current
</NavLink>
</li>
}
このシナリオでのインデックス変数の使用は、NavLink
コンポーネントだけでなく、子コンテンツでループ変数を使用するすべての子コンポーネントで必須です。
または、Enumerable.Range と共に foreach
ループを使用します。
@foreach (var c in Enumerable.Range(0,10))
{
<li ...>
<NavLink ... href="@c">
<span ...></span> @c
</NavLink>
</li>
}
ASP.NET Core エンドポイントのルーティングの統合
このセクションは、回線上で動作する Blazor Web アプリに適用されます。
このセクションは Blazor Server アプリに適用されます。
Blazor Web アプリは ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Program
ファイルで MapRazorComponents を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。 既定のルート コンポーネントは App
コンポーネント (App.razor
) です。
app.MapRazorComponents<App>();
Blazor Server は ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Program
ファイルで MapBlazorHub を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
Blazor Server は ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Startup.Configure
で MapBlazorHub を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。
一般的な構成は、すべての要求を Razor ページにルーティングすることです。これは、Blazor Server アプリのサーバー側部分のホストとして機能します。 慣例により、"ホスト" のページは通常、アプリの Pages
フォルダーでは _Host.cshtml
という名前になります。
ホスト ファイルに指定されるルートは、ルート照合で低い優先順位で動作するため、フォールバック ルートと呼ばれます。 フォールバック ルートは、他のルートが一致しない場合に使用されます。 これにより、Blazor Server アプリのコンポーネント ルーティングに干渉することなく、他のコントローラーやページをアプリで使用できるようになります。
ルート以外の URL のサーバー ホスト用に MapFallbackToPage を構成する方法については、「ASP.NET Core Blazor のホストと展開」をご覧ください。