注
これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。
Warnung
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。
この記事では、 Blazorでページ ナビゲーションをトリガーして処理する方法について説明します。 ユーザーは通常の HTML リンクを使用して異なるページ間を移動できますが、 Blazor はアプリケーション内のナビゲーションを強化して、ページ全体の再読み込みを回避し、よりスムーズなエクスペリエンスを提供します。 NavLink コンポーネントを使用して、リンクが現在のページと一致したときにスタイルを自動的に適用するナビゲーション リンクを作成します。 C# コードでのプログラムによるナビゲーションと URI の管理には、 NavigationManager サービスを使用します。
この記事では、 Blazorでページ ナビゲーションをトリガーして処理する方法について説明します。 NavLink コンポーネントを使用して、リンクが現在のページと一致したときにスタイルを自動的に適用するナビゲーション リンクを作成します。 C# コードでのプログラムによるナビゲーションと URI の管理には、 NavigationManager サービスを使用します。
Important
この記事全体のコード例では、Navigation で呼び出されるメソッドを示します。これはクラスとコンポーネントに挿入される NavigationManager です。
NavLink コンポーネント
ナビゲーション リンクを作成するときは、HTML ハイパーリンク要素 (NavLink) の代わりに <a> コンポーネントを使用します。
NavLink コンポーネントは <a> 要素のように動作しますが、active が現在の URL と一致するかどうかに基づいて href CSS クラスを切り替える点が異なります。
active クラスは、表示されているナビゲーション リンクの中でどのページがアクティブ ページであるかをユーザーが理解するのに役立ちます。 必要に応じて、CSS クラス名を NavLink.ActiveClass に割り当てて、現在のルートが href と一致したときに、レンダリングされるリンクにカスタム CSS クラスを適用します。
BlazorアプリのNavMenuコンポーネント (NavMenu.razor) は、Blazorプロジェクト テンプレートを使用して作成されました。
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
前の例では、HomeNavLinkhref="" はホーム URL と一致し、アプリの既定の基本パス (active) にある / CSS クラスのみを受け取ります。 2 番目のNavLinkは、ユーザーが active で Counter コンポーネントにアクセスしたときに、/counter クラスを受け取ります。
NavLinkMatch 要素の Match 属性に割り当てられる 2 つの <NavLink> オプションがあります。
-
NavLinkMatch.All: NavLink は、現在の URL と一致するとアクティブになり、クエリ文字列とフラグメントは無視されます。 クエリー文字列/フラグメントのマッチングを含めるには、
Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentAppContextスイッチを使用してtrueに設定します。 - NavLinkMatch.Prefix (既定値):NavLink は、現在の URL の任意のプレフィックスに一致する場合にアクティブになります。
カスタムマッチングロジックを採用するには、NavLink サブクラス化し、その ShouldMatch メソッドをオーバーライドします。
true CSS クラスを適用する場合は、メソッドから active を返します。
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute)
{
// Custom matching logic
}
}
NavLinkMatch 要素の Match 属性に割り当てられる 2 つの <NavLink> オプションがあります。
- NavLinkMatch.All: NavLink は、クエリ文字列やフラグメントなど、現在の URL 全体と一致するとアクティブになります。
- NavLinkMatch.Prefix (既定値):NavLink は、現在の URL の任意のプレフィックスに一致する場合にアクティブになります。
追加の NavLink コンポーネント属性は、レンダリングされるアンカー タグに渡されます。 次の例では、NavLink コンポーネントに target 属性が含まれています。
<NavLink href="example-page" target="_blank">Example page</NavLink>
次の HTML マークアップがレンダリングされます。
<a href="example-page" target="_blank">Example page</a>
Warnung
Blazor による子コンテンツのレンダリング方法により、NavLink ループ内の for コンポーネントのレンダリングでは、インクリメントするループ変数が NavLink (子) コンポーネントのコンテンツ内で使用されている場合、ローカル インデックス変数が必要になります。
@for (int c = 1; c < 4; c++)
{
var ct = c;
<li ...>
<NavLink ...>
<span ...></span> Product #@ct
</NavLink>
</li>
}
このシナリオでのインデックス変数の使用は、 コンポーネントだけでなく、子コンテンツでループ変数を使用するNavLink子コンポーネントで必須です。
または、foreach と共に Enumerable.Range ループを使用します。
@foreach (var c in Enumerable.Range(1, 3))
{
<li ...>
<NavLink ...>
<span ...></span> Product #@c
</NavLink>
</li>
}
URI およびナビゲーション状態ヘルパー
C# コード内の URI とナビゲーションを管理するには、NavigationManager を使用します。 NavigationManager には、次の表に示すイベントとメソッドがあります。
| メンバー | Description |
|---|---|
| Uri | 現在の絶対 URI を取得します。 |
| BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの href 要素の <base> 属性に対応します (<head> コンテンツの場所)。 |
| NavigateTo | 指定された URI に移動します。
forceLoad が false の場合:
forceLoad が true の場合:
詳しくは、「ナビゲーションとフォーム処理の強化」セクションをご覧ください。
|
| LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。 |
NotFound |
要求されたリソースが見つからないシナリオを処理するために呼び出されます。 詳細については、「 見つからない応答 」セクションを参照してください。 |
| ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
| ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
RegisterLocationChangingHandler |
受信ナビゲーション イベントを処理するハンドラーを登録します。 NavigateTo を呼び出すと、常にハンドラーが呼び出されます。 |
| GetUriWithQueryParameter | 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
| メンバー | Description |
|---|---|
| Uri | 現在の絶対 URI を取得します。 |
| BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの href 要素の <base> 属性に対応します (<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 を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
| メンバー | Description |
|---|---|
| Uri | 現在の絶対 URI を取得します。 |
| BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの href 要素の <base> 属性に対応します (<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 を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
| メンバー | Description |
|---|---|
| Uri | 現在の絶対 URI を取得します。 |
| BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの href 要素の <base> 属性に対応します (<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 を返します。 詳細については、「クエリ文字列」セクションを参照してください。 |
| メンバー | Description |
|---|---|
| Uri | 現在の絶対 URI を取得します。 |
| BaseUri | 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの href 要素の <base> 属性に対応します (<head> コンテンツの場所)。 |
| NavigateTo | 指定された URI に移動します。
forceLoad が true の場合:
|
| LocationChanged | ナビゲーションの場所が変更されたときに発生するイベントです。 |
| ToAbsoluteUri | 相対 URI を絶対 URI に変換します。 |
| ToBaseRelativePath | アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。 |
場所の変更
LocationChanged イベントの場合、LocationChangedEventArgs でナビゲーション イベントに関する次の情報が提供されます。
- Location:新しい場所の URL。
-
IsNavigationIntercepted:
trueの場合、Blazor によってブラウザーからナビゲーションがインターセプトされました。falseの場合、NavigationManager.NavigateTo によってナビゲーションが発生しました。
次のコンポーネント:
-
Counterを使用してボタンが選択されたときに、アプリのCounter.razorコンポーネント (NavigateTo) に移動します。 -
NavigationManager.LocationChanged をサブスクライブすることで、場所の変更イベントを処理します。
HandleLocationChangedメソッドは、Disposeがフレームワークによって呼び出されると、アンフックになります。 このメソッドをアンフックすることで、コンポーネントのガベージ コレクションが許可されます。ロガーの実装では、ボタンが選択されたときに次の情報をログに記録します。
BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:{PORT}/counter
Navigate.razor:
@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation
<h1>Navigate 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 コア Razor コンポーネント廃棄を参照してください。
静的サーバー側レンダリング中のナビゲーション マネージャーのリダイレクト動作 (静的 SSR)
静的サーバー側レンダリング中のリダイレクト (静的 SSR) の場合、 NavigationManager はフレームワークによってキャプチャされる NavigationException をスローし、エラーをリダイレクトに変換します。 NavigateToの呼び出し後に存在するコードは呼び出されません。 Visual Studio を使用する場合、デバッガーは例外を中断するため、今後のリダイレクトのためにデバッガーが停止するのを回避するために、Visual Studio UI で この例外の種類がユーザー処理される場合に [中断 ] のチェック ボックスをオフにする必要があります。
アプリのプロジェクト ファイルに<BlazorDisableThrowNavigationException>に設定された true MSBuild プロパティを使用して、NavigationExceptionをスローしないようにオプトインできます。 また、 NavigateTo の呼び出し後のコードは、以前に実行されなかったときに実行されます。 この動作は、.NET 10 以降の Blazor Web App プロジェクト テンプレートで既定で有効になっています。
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
注
.NET 10 以降では、NavigationException MSBuild プロパティをアプリのプロジェクト ファイルに<BlazorDisableThrowNavigationException>に設定することで、trueをスローしないようにオプトインできます。 新しい MSBuild プロパティと動作を利用するには、アプリを .NET 10 以降にアップグレードします。
見つからない応答
NavigationManager には、静的サーバー側レンダリング (静的 SSR) またはグローバル対話型レンダリング中に要求されたリソースが見つからないシナリオを処理する NotFound メソッドが用意されています。
静的 SSR: 呼び出し
NavigationManager.NotFoundは、HTTP 状態コードを 404 に設定します。対話型レンダリング: Blazor ルーター (
Routerコンポーネント) に通知して、見つからないコンテンツをレンダリングします。ストリーミング レンダリング: 拡張ナビゲーション がアクティブな場合、 ストリーミング レンダリング はページを再読み込みせずに Not Found コンテンツをレンダリングします。 拡張ナビゲーションがブロックされると、フレームワークはページ更新を使用して Not Found コンテンツにリダイレクトします。
注
次の説明では、Not Found Razor コンポーネントを Router コンポーネントの NotFoundPage パラメーターに割り当てることができることを説明します。 パラメーターは NavigationManager.NotFound と連携して動作し、このセクションの後半で詳しく説明します。
ストリーミング レンダリングでは、 NotFoundPage 割り当て (NotFoundPage="...") や ステータス コード ページの再実行ミドルウェア ページの割り当て (UseStatusCodePagesWithReExecute) など、ルートを持つコンポーネントのみをレンダリングできます。
DefaultNotFound 404 コンテンツ ("Not found" プレーン テキスト) にはルートがないため、ストリーミング レンダリング中に使用することはできません。
注
Not Found レンダー フラグメント (<NotFound>...</NotFound>) は、.NET 10 以降ではサポートされていません。
NavigationManager.NotFound コンテンツ レンダリングでは、応答が開始されたかどうかに関係なく (順番に) 次のものが使用されます。
- NotFoundEventArgs.Pathが設定されている場合は、割り当てられたページの内容をレンダリングします。
-
Router.NotFoundPageが設定されている場合は、割り当てられたページをレンダリングします。 - 状態コード ページの再実行ミドルウェア ページ (構成されている場合)。
- 上記の方法が採用されていない場合は、アクションはありません。
ブラウザーベースのアドレス ルーティングの問題 (ブラウザーのアドレス バーに入力された不適切な URL、アプリにエンドポイントがないリンクの選択など) には、を使用したUseStatusCodePagesWithReExecuteが優先されます。
コンポーネントが静的にレンダリングされ (静的 SSR)、 NavigationManager.NotFound が呼び出されると、応答に 404 状態コードが設定されます。
@page "/render-not-found-ssr"
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
Navigation.NotFound();
}
}
グローバル対話型レンダリングに Not Found コンテンツを提供するには、Not Found ページ (Razor コンポーネント) を使用します。
注
Blazor プロジェクト テンプレートには、NotFound.razor ページが含まれています。 このページは、 NavigationManager.NotFound が呼び出されるたびに自動的にレンダリングされるため、一貫したユーザー エクスペリエンスで不足しているルートを処理できます。
Pages/NotFound.razor:
@page "/not-found"
@layout MainLayout
<h3>Not Found</h3>
<p>Sorry, the content you are looking for does not exist.</p>
NotFound コンポーネントは、ルーターの NotFoundPage パラメーターに割り当てられます。
NotFoundPage では、Blazor 以外のミドルウェアを含む、ステータス コード ページの再実行ミドルウェア全体で使用できるルーティングがサポートされています。
次の例では、上記の NotFound コンポーネントがアプリの Pages フォルダーに存在し、 NotFoundPage パラメーターに渡されます。
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
コンポーネントがグローバルインタラクティブレンダリングモードでレンダリングされると、 NavigationManager.NotFound を呼び出すと、 Blazor ルータに NotFound コンポーネントがレンダリングされます。
@page "/render-not-found-interactive"
@inject NavigationManager Navigation
@if (RendererInfo.IsInteractive)
{
<button @onclick="TriggerNotFound">Trigger Not Found</button>
}
@code {
private void TriggerNotFound()
{
Navigation.NotFound();
}
}
OnNotFoundが呼び出されたときに通知にNavigationManager.NotFound イベントを使用できます。 イベントは、404 応答ではなく、 NavigationManager.NotFound が呼び出されたときにのみ発生します。 たとえば、 HttpContextAccessor.HttpContext.Response.StatusCode を 404 に設定しても、 NavigationManager.NotFound/OnNotFoundはトリガーされません。
カスタム ルーターを実装するアプリでも、 NavigationManager.NotFoundを使用できます。 カスタム ルーターは、応答の状態に応じて、次の 2 つのソースから Not Found コンテンツをレンダリングできます。
応答の状態に関係なく、ページへの再実行パスは、 UseStatusCodePagesWithReExecuteに渡すことによって使用できます。
app.UseStatusCodePagesWithReExecute( "/not-found", createScopeForStatusCodePages: true);応答が開始されると、ルーターのNotFoundEventArgs.Pathをサブスクライブすることで、
OnNotFoundEventを使用できます。@code { [CascadingParameter] public HttpContext? HttpContext { get; set; } private void OnNotFoundEvent(object sender, NotFoundEventArgs e) { // Only execute the logic if HTTP response has started, // because setting NotFoundEventArgs.Path blocks re-execution if (HttpContext?.Response.HasStarted == false) { return; } var type = typeof(CustomNotFoundPage); var routeAttributes = type.GetCustomAttributes<RouteAttribute>(inherit: true); if (routeAttributes.Length == 0) { throw new InvalidOperationException($"The type {type.FullName} " + $"doesn't have a {nameof(RouteAttribute)} applied."); } var routeAttribute = (RouteAttribute)routeAttributes[0]; if (routeAttribute.Template != null) { e.Path = routeAttribute.Template; } } }
対話型サーバー側レンダリング (対話型 SSR) を採用するコンポーネントの次の例では、カスタム コンテンツは、OnNotFoundが呼び出される場所に応じてレンダリングされます。 コンポーネントの初期化時にムービーが見つからないときに、次の Movie コンポーネントによってイベントがトリガーされた場合、カスタム メッセージは、要求されたムービーが見つからないことを示します。 次の例の User コンポーネントによってイベントがトリガーされた場合、ユーザーが見つからないことを示す別のメッセージが表示されます。
次の NotFoundContext サービスは、コンポーネントによってコンテンツが見つからない場合のコンテキストとメッセージを管理します。
NotFoundContext.cs:
public class NotFoundContext
{
public string? Heading { get; private set; }
public string? Message { get; private set; }
public void UpdateContext(string heading, string message)
{
Heading = heading;
Message = message;
}
}
サービスは、サーバー側の Program ファイルに登録されます。
builder.Services.AddScoped<NotFoundContext>();
NotFound ページでは、NotFoundContextが挿入され、見出しとメッセージが表示されます。
Pages/NotFound.razor:
@page "/not-found"
@layout MainLayout
@inject NotFoundContext NotFoundContext
<h3>@NotFoundContext.Heading</h3>
<div>
<p>@NotFoundContext.Message</p>
</div>
Routes コンポーネント (Routes.razor) は、NotFound パラメーターを使用して、NotFoundPage コンポーネントを Not Found ページとして設定します。
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
...
</Router>
次のコンポーネント例では、
-
NotFoundContextと共に、NavigationManager サービスが挿入されます。 -
OnInitializedAsyncでは、
HandleNotFoundは、OnNotFoundイベントに割り当てられたイベント ハンドラーです。HandleNotFoundはNotFoundContext.UpdateContextを呼び出して、NotFoundコンポーネントの Not Found コンテンツの見出しとメッセージを設定します。 - コンポーネントは通常、ルート パラメーターから ID を使用して、データベースなどのデータ ストアからムービーまたはユーザーを取得します。 次の例では、エンティティが見つからない場合の動作をシミュレートするためにエンティティが返されません (
null)。 - エンティティが OnInitializedAsyncに返されない場合、
NavigationManager.NotFoundが呼び出され、OnNotFoundイベントとHandleNotFoundイベント ハンドラーがトリガーされます。 見つからないコンテンツはルーターによって表示されます。 -
HandleNotFoundメソッドは、IDisposable.Disposeでのコンポーネントの破棄時にフック解除されます。
Movie コンポーネント (Movie.razor):
@page "/movie/{Id:int}"
@implements IDisposable
@inject NavigationManager NavigationManager
@inject NotFoundContext NotFoundContext
<div>
No matter what ID is used, no matching movie is returned
from the call to GetMovie().
</div>
@code {
[Parameter]
public int Id { get; set; }
protected override async Task OnInitializedAsync()
{
NavigationManager.OnNotFound += HandleNotFound;
var movie = await GetMovie(Id);
if (movie == null)
{
NavigationManager.NotFound();
}
}
private void HandleNotFound(object? sender, NotFoundEventArgs e)
{
NotFoundContext.UpdateContext("Movie Not Found",
"Sorry! The requested movie wasn't found.");
}
private async Task<MovieItem[]?> GetMovie(int id)
{
// Simulate no movie with matching id found
return await Task.FromResult<MovieItem[]?>(null);
}
void IDisposable.Dispose()
{
NavigationManager.OnNotFound -= HandleNotFound;
}
public class MovieItem
{
public int Id { get; set; }
public string? Title { get; set; }
}
}
User コンポーネント (User.razor):
@page "/user/{Id:int}"
@implements IDisposable
@inject NavigationManager NavigationManager
@inject NotFoundContext NotFoundContext
<div>
No matter what ID is used, no matching user is returned
from the call to GetUser().
</div>
@code {
[Parameter]
public int Id { get; set; }
protected override async Task OnInitializedAsync()
{
NavigationManager.OnNotFound += HandleNotFound;
var user = await GetUser(Id);
if (user == null)
{
NavigationManager.NotFound();
}
}
private void HandleNotFound(object? sender, NotFoundEventArgs e)
{
NotFoundContext.UpdateContext("User Not Found",
"Sorry! The requested user wasn't found.");
}
private async Task<UserItem[]?> GetUser(int id)
{
// Simulate no user with matching id found
return await Task.FromResult<UserItem[]?>(null);
}
void IDisposable.Dispose()
{
NavigationManager.OnNotFound -= HandleNotFound;
}
public class UserItem
{
public int Id { get; set; }
public string? Name { get; set; }
}
}
テスト アプリを使用してローカル デモの上記のコンポーネントに到達するには、 NavMenu コンポーネント (NavMenu.razor) にエントリを作成して、 Movie および User コンポーネントに到達します。 次の例では、ルート パラメーターとして渡されるエンティティ ID は、実際にはコンポーネントによって使用されないため、効果のないモック値であり、ムービーやユーザーが見つからないことをシミュレートします。
NavMenu.razorの場合:
<div class="nav-item px-3">
<NavLink class="nav-link" href="movie/1">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Movie
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="user/2">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> User
</NavLink>
</div>
ナビゲーションとフォーム処理の強化
このセクションは Blazor Web App に適用されます。
Blazor Web App は、ページ ナビゲーションとフォーム処理の要求に対して 2 種類のルーティングを実行できます。
- 通常のナビゲーション (ドキュメント間のナビゲーション): 要求 URL に対してページ全体の再読み込みがトリガーされます。
- 拡張ナビゲーション (同じドキュメントのナビゲーション): Blazor は要求をインターセプトし、代わりに
fetch要求を実行します。 その後、Blazor は応答の内容をページの DOM にパッチします。 Blazor の強化されたナビゲーションとフォーム処理により、ページ全体の再読み込みが不要になり、ページの状態が維持されるため、ページの読み込みが速くなり、通常はページ上のユーザーのスクロール位置が失われません。
拡張ナビゲーションは、次の場合に使用できます。
-
Blazor Web App スクリプト (
blazor.web.js) または Blazor Server スクリプト (blazor.server.js) ではなく、Blazor WebAssembly スクリプト (blazor.webassembly.js) が使われます。 - この機能が明示的に無効にされることはありません。
- 宛先 URL は内部ベース URI 空間 (アプリのベース パス) 内にあり、ページへのリンクには
data-enhance-nav属性がfalseに設定されていません。
サーバー側のルーティングと拡張ナビゲーションが有効になっている場合、場所変更ハンドラーは、対話型ランタイムから開始したプログラム ナビゲーションに対してのみ呼び出されます。 今後のリリースでは、リンクをたどるなど、ナビゲーションの種類が追加されると、場所変更ハンドラーが呼び出される可能性もあります。
拡張ナビゲーションが発生すると、通常は、対話型サーバーと WebAssembly ランタイムに登録されている LocationChanged イベント ハンドラーが呼び出されます。 場所変更ハンドラーが拡張ナビゲーションをインターセプトしない場合があります。 たとえば、対話型ランタイムが使用可能になる前に、ユーザーが別のページに切り替えることがあります。 そのため、場所変更ハンドラーが実行される保証がないため、アプリ ロジックがこのハンドラーの呼び出しに依存しないことが重要です。
NavigateTo を呼び出すとき:
-
forceLoadがfalseの場合、これが既定値です。- また、拡張ナビゲーションは現在の URL で使用でき、Blazor の拡張ナビゲーションがアクティブにされます。
- そうでない場合、Blazor は要求された URL のページ全体の再読み込みを実行します。
-
forceLoadがtrueの場合: 拡張ナビゲーションが使用可能かどうかに関係なく、Blazor は要求された URL のページ全体の再読み込みを実行します。
NavigationManager.Refresh(bool forceLoad = false) を呼び出すことで、現在のページを更新できます。その場合、常に拡張ナビゲーションが実行されます (使用可能な場合)。 拡張ナビゲーションを使用できない場合、Blazor はページ全体の再読み込みを実行します。
Navigation.Refresh();
拡張ナビゲーションが使用可能な場合でも、true を forceLoad パラメーターに渡して、ページ全体の再読み込みが常に実行されるようにします。
Navigation.Refresh(true);
拡張ナビゲーションは既定で有効になっていますが、data-enhance-nav HTML 属性を使用して階層的およびリンクごとに制御できます。
次の例では、拡張ナビゲーションを無効にします。
<a href="redirect" data-enhance-nav="false">
GET without enhanced navigation
</a>
<ul data-enhance-nav="false">
<li>
<a href="redirect">GET without enhanced navigation</a>
</li>
<li>
<a href="redirect-2">GET without enhanced navigation</a>
</li>
</ul>
宛先がBlazor 以外のエンドポイントの場合、拡張ナビゲーションは適用されません。クライアント側の JavaScript は、ページ全体の読み込みとして再試行します。 これにより、既存のページに修正プログラムを適用すべきではない外部ページについて、フレームワークに混乱を招くことはありません。
拡張フォーム処理を有効にするには、Enhance フォームに EditForm パラメーターを追加するか、HTML フォーム (data-enhance) に <form> 属性を追加します。
<EditForm ... Enhance ...>
...
</EditForm>
<form ... data-enhance ...>
...
</form>
拡張フォーム処理は階層構造ではなく、子フォームに流れることはありません。
サポート外: フォームの先祖要素に拡張ナビゲーションを設定して、フォームの拡張ナビゲーションを有効にすることはできません。
<div ... data-enhance ...>
<form ...>
<!-- NOT enhanced -->
</form>
</div>
拡張フォームの投稿は、Blazor エンドポイントでのみ機能します。 拡張フォームを Blazor 以外のエンドポイントに POST すると、エラーが発生します。
拡張ナビゲーションを無効にするには:
-
EditForm の場合は、フォーム要素から Enhance パラメーターを削除します (または、それを
falseに設定します:Enhance="false")。 - HTML
<form>の場合は、フォーム要素からdata-enhance属性を削除します (または、それをfalseに設定します:data-enhance="false")。
更新された内容がサーバー レンダリングの一部でない場合、Blazor の拡張ナビゲーションとフォーム処理は DOM に対する動的な変更を元に戻す可能性があります。 要素の内容を保持するには、data-permanent 属性を使用します。
次の例では、ページが読み込まれるときに、<div> 要素の内容がスクリプトによって動的に更新されます。
<div data-permanent>
...
</div>
クライアントで Blazor を開始したら、enhancedload イベントを使用して拡張ページの更新をリッスンできます。 これにより、強化されたページ更新によって元に戻された可能性がある変更を DOM に再適用できます。
Blazor.addEventListener('enhancedload', () => console.log('Enhanced update!'));
拡張ナビゲーションとフォーム処理をグローバルに無効にするには、「ASP.NET Core Blazor の起動」をご覧ください。
静的サーバー側レンダリング (静的 SSR) による拡張ナビゲーションでは、JavaScript を読み込むときに特別な注意が必要です。 詳細については、「静的サーバー側レンダリング (静的 SSR) を使用した ASP.NET Core Blazor JavaScript」を参照してください。
ベース URI プレフィックスに対する相対 URI を生成する
ToBaseRelativePath は、アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。
次の例を確認してください。
try
{
baseRelativePath = Navigation.ToBaseRelativePath(inputURI);
}
catch (ArgumentException ex)
{
...
}
アプリのベース URI が https://localhost:8000 の場合、次の結果が得られます。
-
https://localhost:8000/segmentでinputURIを渡すと、baseRelativePathのsegmentになります。 -
https://localhost:8000/segment1/segment2でinputURIを渡すと、baseRelativePathのsegment1/segment2になります。
アプリのベース URI が inputURI のベース URI と一致しない場合は、ArgumentException がスローされます。
https://localhost:8001/segment で inputURI を渡すと、次の例外が発生します。
System.ArgumentException: 'The URI 'https://localhost:8001/segment' is not contained by the base URI 'https://localhost:8000/'.'
ナビゲーション履歴の状態
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} プレースホルダーに対して異なるクエリ パラメーター名が指定されています。
コンポーネント パラメータ プロパティ ([Parameter]) とは異なり、[SupplyParameterFromQuery] プロパティは private に加えて public としてマークできます。
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
private string? {COMPONENT PARAMETER NAME} { get; set; }
コンポーネント パラメーター プロパティ ([Parameter]) と同様に、[SupplyParameterFromQuery] プロパティは常に .NET 6/7 で public プロパティです。 .NET 8 以降では、[SupplyParameterFromQuery] プロパティは public または private としてマークできます。
[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.razor コンポーネント用の Search の場合)。
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]
private string? Filter { get; set; }
[SupplyParameterFromQuery]
private int? Page { get; set; }
[SupplyParameterFromQuery(Name = "star")]
private 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; }
}
GetUriWithQueryParameter を使用して、現在の URL の 1 つ以上のクエリ パラメーターを追加、変更、または削除します。
@inject NavigationManager Navigation
...
Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})
前の例の場合:
-
{NAME}プレースホルダーでは、クエリ パラメーター名が指定されます。{VALUE}プレースホルダーでは、サポートされている型として値が指定されます。 サポートされている型の一覧については、このセクションで後述します。 - 1 つのパラメーターを含む現在の URL と等しい文字列が返されます。
- クエリ パラメーター名が現在の URL に存在しない場合に追加されます。
- クエリ パラメーターが現在の URL に存在する場合、指定された値に更新されます。
- 指定された値の型が Null 許容で、値が
nullの場合は、削除されます。
- 指定された型に対して、カルチャに左右されない適切な書式設定が適用されます (CultureInfo.InvariantCulture)。
- クエリ パラメーターの名前と値は URL エンコードされます。
- 型のインスタンスが複数ある場合、一致するクエリ パラメーター名を持つすべての値が置き換えられます。
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>です。
サポートされている型は、ルート制約でサポートされている型と同じです。
boolDateOnlyDateTimedecimaldoublefloatGuidintlongstringTimeOnly
サポートされる型には次のようなものがあります。
- 上記の型の Null 許容バリアント。
- 上記の型の配列 (Null 許容、Null 非許容どちらでも)。
Warnung
既定で有効になっている圧縮を使って、信頼されていないソースからのデータをレンダリングする、セキュリティで保護された (認証済み/承認済み) 対話型サーバー側コンポーネントは作成しないでください。 信頼されていないソースには、ルート パラメータ、クエリ文字列、JS 相互運用からのデータ、サードパーティのユーザーがコントロールできる他のデータ ソース (データベース、外部サービス) が含まれます。 詳細については、「ASP.NET Core BlazorSignalR ガイダンス」と「対話型サーバー側の ASP.NET Core Blazor レンダリングの脅威軽減策に関するガイダンス」を参照してください。
パラメーターが存在する場合にクエリ パラメーター値を置き換える
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 を呼び出し、値
nameを使用してMorena Baccarinクエリ パラメーターを追加または置き換えます。 - NavigateTo を呼び出して、新しい URL へのナビゲーションをトリガーします。
Navigation.NavigateTo(
Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));
要求のクエリ文字列は、NavigationManager.Uri プロパティから取得されます。
@inject NavigationManager Navigation
...
var query = new Uri(Navigation.Uri).Query;
クエリ文字列のパラメーターを解析するための 1 つの方法は、URLSearchParamsで JS を使用することです。
export createQueryString = (string queryString) => new URLSearchParams(queryString);
JavaScript モジュールを使用した JavaScript の分離について詳しくは、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。
名前付き要素に移動する
要素へのハッシュされた (#) 参照による次の方法を使って、名前付き要素に移動します。 コンポーネント内の要素へのルートと、外部コンポーネント内の要素へのルートでは、ルート (root) 相対パスが使われます。 先頭のスラッシュ (/) は省略できます。
次の各方法の例では、id コンポーネント内の targetElement が Counter の要素への移動を示します。
アンカー要素 (
<a>) とhref:<a href="/counter#targetElement">NavLink コンポーネントと
href:<NavLink href="/counter#targetElement">相対 URL を渡す NavigationManager.NavigateTo:
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>
次の FragmentRouting コンポーネントをアプリに追加します。
FragmentRouting.razor:
@page "/fragment-routing"
@inject NavigationManager Navigation
<PageTitle>Fragment routing</PageTitle>
<h1>Fragment routing to named elements</h1>
<ul>
<li>
<a href="/fragment-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="/fragment-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");
}
}
場所の変更を処理/防止する
RegisterLocationChangingHandler では、受信ナビゲーション イベントを処理するハンドラーを登録します。 LocationChangingContext によって提供されるハンドラーのコンテキストには、次のプロパティが含まれます。
- TargetLocation: ターゲットの場所を取得します。
- HistoryEntryState: ターゲット履歴エントリに関連付けられている状態を取得します。
- IsNavigationIntercepted: ナビゲーションがリンクからインターセプトされたかどうかを取得します。
- CancellationToken: ナビゲーションが取り消されたかどうかを判断する CancellationToken を取得します。たとえば、ユーザーが別のナビゲーションをトリガーしたかどうかを判断します。
- PreventNavigation: ナビゲーションが続行されないようにするために呼び出されます。
コンポーネントでは、OnAfterRender{Async} ライフサイクル メソッドに複数の場所変更ハンドラーを登録できます。 ナビゲーションでは、(複数のコンポーネントにまたがって) アプリ全体に登録されているすべての場所変更ハンドラーを呼び出し、すべての内部ナビゲーションでそれらをすべて並列で実行します。
NavigateTo に加え、次の場合にハンドラーが呼び出されます。
- 内部リンクを選択する場合。これは、アプリのベース パスの下にある URL を指すリンクです。
- ブラウザーの [進む] ボタンと [戻る] ボタンを使用して移動する場合。
ハンドラーは、アプリ内の内部ナビゲーションに対してのみ実行されます。 ユーザーが別のサイトに移動するリンクを選択した場合、またはアドレス バーを別のサイトに手動で変更した場合、場所変更ハンドラーは実行されません。
IDisposable を実装し、登録済みハンドラーを破棄して登録を解除します。 詳細については、ASP.NET Core Razor コンポーネントの破棄を参照してください。
Important
場所の変更を処理するときは、JavaScript (JS) 相互運用機能を使って DOM のクリーンアップ タスクを実行しないでください。 クライアントで MutationObserver パターン を JS 使用します。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。
次の例では、ナビゲーション イベントの場所変更ハンドラーが登録されています。
NavHandler.razor:
@page "/nav-handler"
@implements IDisposable
@inject NavigationManager Navigation
<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 つの選択として扱われる場合があります。
- ナビゲーションが完了する前にユーザーが複数のリンクを選択した場合、最後に選択したリンクによってナビゲーションが決まります。
ナビゲーション履歴スタックのエントリと状態を制御するために NavigationOptions に NavigateTo を渡す方法の詳細については、「ナビゲーション オプション」セクションを参照してください。
その他のコード例については、「NavigationManagerComponent」(BasicTestApp 参照ソース) の dotnet/aspnetcore に関するページを参照してください。
注
通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。
表示されている限り NavigationLock コンポーネントはナビゲーション イベントをインターセプトし、続行またはキャンセルの決定が行われるまで、特定のナビゲーションを効果的に "ロック" します。 ナビゲーション インターセプトのスコープをコンポーネントの有効期間に設定できる場合、NavigationLock を使用します。
NavigationLock パラメーター:
-
ConfirmExternalNavigation では、外部ナビゲーションの確認またはキャンセルをユーザーに求めるブラウザー ダイアログを設定します。 既定値は
falseです。 確認ダイアログを表示するには、ブラウザーのアドレス バーの URL を使用して外部ナビゲーションをトリガーする前に、最初にユーザーがページを操作する必要があります。 操作の要件について詳しくは、「Window:beforeunloadイベント」 を参照してください。 - OnBeforeInternalNavigation では、内部ナビゲーション イベントのコールバックを設定します。
次の NavLock コンポーネントでは、以下のことを行います。
-
https://www.microsoft.comへのナビゲーションを成功させるには、Microsoft の Web サイトに従う試みをユーザーが確認する必要があります。 - PreventNavigation は、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();
}
}
}
その他のコード例については、「ConfigurableNavigationLock」(BasicTestApp 参照ソース) の dotnet/aspnetcore コンポーネントに関するページを参照してください。
リフレクションを使用して動的に生成された NavLink コンポーネント
NavLink コンポーネントのエントリは、リフレクションを介してアプリのコンポーネントから動的に作成できます。 次の例では、さらにカスタマイズするための一般的なアプローチを示します。
次のデモでは、アプリのコンポーネントに一貫した標準の名前付け規則が使用されています。
- ルーティング可能なコンポーネント ファイル名では、Pascal 形式† (例:
Pages/ProductDetail.razor) が使用されます。 - ルーティング可能なコンポーネント ファイル パスは、コンポーネントのルート テンプレート内の単語間に表示されるハイフン付きのケバブ ケース‡ の URL に一致します。 たとえば、ルート テンプレートが
ProductDetail(/product-detail) の@page "/product-detail"コンポーネントは、ブラウザーの相対 URL/product-detailで要求されます。
† パスカル ケース (大文字のキャメル ケース) は、スペースと句読点を使用せず、大文字の各単語の最初の文字 (最初の単語を含む) を使用する名前付け規則です。
ケバブ ケースは、単語間の小文字とダッシュを使用するスペースと句読点のない名前付け規則です。
既定の Razor ページの NavMenu コンポーネント (NavMenu.razor) の Home マークアップでは、NavLink コンポーネントがコレクションから追加されます。
<div class="nav-scrollable"
onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu"
aria-hidden="true"></span> Home
</NavLink>
</div>
+ @foreach (var name in GetRoutableComponents())
+ {
+ <div class="nav-item px-3">
+ <NavLink class="nav-link"
+ href="@Regex.Replace(name, @"(\B[A-Z]|\d+)", "-$1").ToLower()">
+ @Regex.Replace(name, @"(\B[A-Z]|\d+)", " $1")
+ </NavLink>
+ </div>
+ }
</nav>
</div>
GetRoutableComponents ブロックの @code メソッド:
public IEnumerable<string> GetRoutableComponents() =>
Assembly.GetExecutingAssembly()
.ExportedTypes
.Where(t => t.IsSubclassOf(typeof(ComponentBase)))
.Where(c => c.GetCustomAttributes(inherit: true)
.OfType<RouteAttribute>()
.Any())
.Where(c => c.Name != "Home" && c.Name != "Error")
.OrderBy(o => o.Name)
.Select(c => c.Name);
上記の例には、コンポーネントの表示されるリストの次のページは含まれていません。
-
Homeページ: ページは、自動的に生成されたリンクとは別に一覧表示されます。これは、ページが一覧の上部に表示され、Matchパラメーターが設定される必要があるためです。 -
Errorページ: エラー ページはフレームワークによってのみナビゲートされ、一覧には表示されません。
サンプル アプリでの上記のコードのデモンストレーションについては、 Blazor Web App または Blazor WebAssembly サンプル アプリを取得します。
ASP.NET Core