From 6fe6c8beff75318e04bdb727853d3fc3f87ebe22 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Fri, 2 Sep 2022 07:27:26 -0500 Subject: [PATCH 01/17] Routing features (loc changes, NavigationLock) --- aspnetcore/blazor/fundamentals/routing.md | 114 ++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index b4d7dd0547f2..b3392f78cca4 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1543,8 +1543,122 @@ Use to manage URIs and | | An event that fires when the navigation location has changed. For more information, see the [Location changes](#location-changes) section. | | | Converts a relative URI into an absolute URI. | | | Given a base URI (for example, a URI previously returned by ), converts an absolute URI into a URI relative to the base URI prefix. | +| `RegisterLocationChangingHandler` | Registers a handler to process incoming navigation events. | | | Returns a URI constructed by updating with a single parameter added, updated, or removed. For more information, see the [Query strings](#query-strings) section. | +In the following example, a location changing handler is registered for navigation events. Note that calling always invokes any location changing handlers that exist. + +`Pages/NavHandler.razor`: + +```razor +@page "/nav-handler" +@inject NavigationManager NavigationManager + +

+ + +

+ +@code { + protected override void OnInitialized() + { + NavigationManager.RegisterLocationChangingHandler(OnLocationChanging); + } + + private async ValueTask OnLocationChanging(LocationChangingContext context) + { + if (context.TargetLocation == "counter") + { + context.PreventNavigation(); + } + } +} +``` + +`LocationChangingContext` properties: + +* `TargetLocation`: Obtain the target location. +* `HistoryEntryState`: The state associated with the target history entry. +* `IsNavigationIntercepted`: Whether the navigation was intercepted from a link. +* `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. +* `PreventNavigation`: Called to prevent the navigation from continuing. + +Pass `NavigationOptions` to to control the following behaviors: + +* `ForceLoad`: Bypass client-side routing and force the browser to load the new page from the server, whether or not the URI is handled by the client-side router. The default value is `false`. +* `ReplaceHistoryEntry`: Replace the current entry in the history stack. If `false`, append the new entry to the history stack. The default value is `false`. +* `HistoryEntryState`: Gets or sets the state to append to the history entry. + +```csharp +NavigationManager.NavigateTo("/path", new NavigationOptions +{ + HistoryEntryState = "Navigation state", +}); +``` + +Since internal navigation can be canceled asynchronously, multiple overlapping navigations may occur. For example, the user is rapidly clicking the back button on a page or clicking multiple links before a navigation is determined. The following is a summary of the asynchronous navigation logic: + +* If any location changing handlers are registered, all navigations are initially reverted, then replayed if the navigation isn't canceled. +* If overlapping navigation requests are made, the latest request always cancels earlier requests, which means the following: + * The app may treat multiple back and forward button clicks as a single click. + * If the user clicks multiple links before the navigation completes, the last link clicked determines the navigation. + +For more information, see the following components in the `BasicTestApp` (`dotnet/aspnetcore` reference source): + +* [`NavigationManagerComponent`](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/NavigationManagerComponent.razor) +* [`ConfigurableNavigationLock`](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/ConfigurableNavigationLock.razor) + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +## Intercept location changes in a component + +The `NavigationLock` component is used to intercept navigation events: + +* The component only takes effect after it's rendered, just like any other Razor component. +* It's possible for the suer to bypass interception of the navigation because the user navigated away before the component had a chance to render. + +In the following `NavigationLockExample` component: + +* `OnBeforeInternalNavigation` sets a callback to be invoked when an internal navigation event occurs. `PreventNavigation` is called to prevent naviation from occurring if the user declines to confirm the navigation via a [JavaScript (JS) interop call](xref:blazor/js-interop/call-javascript-from-dotnet) that spawns the [JS `confirm` dialog](https://developer.mozilla.org/docs/Web/API/Window/confirm). +* `ConfirmExternalNavigation` sets a browser dialog to prompt the user to either confirm or cancel the external navigation. The default value is `false`. + +`Pages/NavLock.razor`: + +```razor +@page "/nav-lock" +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager + +

+ +

+ + + +@code { + private void Navigate() + { + NavigationManager.NavigateTo("/"); + } + + private async Task OnBeforeInternalNavigation(LocationChangingContext context) + { + var isConfirmed = await JSRuntime.InvokeAsync("confirm", + "Are you sure you want to navigate to the Index page?"); + + if (!isConfirmed) + { + context.PreventNavigation(); + } + } +} +``` + ## Location changes For the event, provides the following information about navigation events: From 6d3a721a82587036e2297bda13bf0b7dd293bf5c Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Sep 2022 07:07:42 -0500 Subject: [PATCH 02/17] Updates --- aspnetcore/blazor/fundamentals/routing.md | 236 +++++++++++----------- 1 file changed, 122 insertions(+), 114 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index b3392f78cca4..3f89c66c4a3a 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1543,122 +1543,9 @@ Use to manage URIs and | | An event that fires when the navigation location has changed. For more information, see the [Location changes](#location-changes) section. | | | Converts a relative URI into an absolute URI. | | | Given a base URI (for example, a URI previously returned by ), converts an absolute URI into a URI relative to the base URI prefix. | -| `RegisterLocationChangingHandler` | Registers a handler to process incoming navigation events. | +| [`RegisterLocationChangingHandler`](#handle-location-changes-in-a-component) | Registers a handler to process incoming navigation events. Calling always invokes the handler. | | | Returns a URI constructed by updating with a single parameter added, updated, or removed. For more information, see the [Query strings](#query-strings) section. | -In the following example, a location changing handler is registered for navigation events. Note that calling always invokes any location changing handlers that exist. - -`Pages/NavHandler.razor`: - -```razor -@page "/nav-handler" -@inject NavigationManager NavigationManager - -

- - -

- -@code { - protected override void OnInitialized() - { - NavigationManager.RegisterLocationChangingHandler(OnLocationChanging); - } - - private async ValueTask OnLocationChanging(LocationChangingContext context) - { - if (context.TargetLocation == "counter") - { - context.PreventNavigation(); - } - } -} -``` - -`LocationChangingContext` properties: - -* `TargetLocation`: Obtain the target location. -* `HistoryEntryState`: The state associated with the target history entry. -* `IsNavigationIntercepted`: Whether the navigation was intercepted from a link. -* `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. -* `PreventNavigation`: Called to prevent the navigation from continuing. - -Pass `NavigationOptions` to to control the following behaviors: - -* `ForceLoad`: Bypass client-side routing and force the browser to load the new page from the server, whether or not the URI is handled by the client-side router. The default value is `false`. -* `ReplaceHistoryEntry`: Replace the current entry in the history stack. If `false`, append the new entry to the history stack. The default value is `false`. -* `HistoryEntryState`: Gets or sets the state to append to the history entry. - -```csharp -NavigationManager.NavigateTo("/path", new NavigationOptions -{ - HistoryEntryState = "Navigation state", -}); -``` - -Since internal navigation can be canceled asynchronously, multiple overlapping navigations may occur. For example, the user is rapidly clicking the back button on a page or clicking multiple links before a navigation is determined. The following is a summary of the asynchronous navigation logic: - -* If any location changing handlers are registered, all navigations are initially reverted, then replayed if the navigation isn't canceled. -* If overlapping navigation requests are made, the latest request always cancels earlier requests, which means the following: - * The app may treat multiple back and forward button clicks as a single click. - * If the user clicks multiple links before the navigation completes, the last link clicked determines the navigation. - -For more information, see the following components in the `BasicTestApp` (`dotnet/aspnetcore` reference source): - -* [`NavigationManagerComponent`](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/NavigationManagerComponent.razor) -* [`ConfigurableNavigationLock`](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/ConfigurableNavigationLock.razor) - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -## Intercept location changes in a component - -The `NavigationLock` component is used to intercept navigation events: - -* The component only takes effect after it's rendered, just like any other Razor component. -* It's possible for the suer to bypass interception of the navigation because the user navigated away before the component had a chance to render. - -In the following `NavigationLockExample` component: - -* `OnBeforeInternalNavigation` sets a callback to be invoked when an internal navigation event occurs. `PreventNavigation` is called to prevent naviation from occurring if the user declines to confirm the navigation via a [JavaScript (JS) interop call](xref:blazor/js-interop/call-javascript-from-dotnet) that spawns the [JS `confirm` dialog](https://developer.mozilla.org/docs/Web/API/Window/confirm). -* `ConfirmExternalNavigation` sets a browser dialog to prompt the user to either confirm or cancel the external navigation. The default value is `false`. - -`Pages/NavLock.razor`: - -```razor -@page "/nav-lock" -@inject IJSRuntime JSRuntime -@inject NavigationManager NavigationManager - -

- -

- - - -@code { - private void Navigate() - { - NavigationManager.NavigateTo("/"); - } - - private async Task OnBeforeInternalNavigation(LocationChangingContext context) - { - var isConfirmed = await JSRuntime.InvokeAsync("confirm", - "Are you sure you want to navigate to the Index page?"); - - if (!isConfirmed) - { - context.PreventNavigation(); - } - } -} -``` - ## Location changes For the event, provides the following information about navigation events: @@ -1681,6 +1568,23 @@ The following component: For more information on component disposal, see . +## Navigation options + +Pass `NavigationOptions` to to control the following behaviors: + +* `ForceLoad`: Bypass client-side routing and force the browser to load the new page from the server, whether or not the URI is handled by the client-side router. The default value is `false`. +* `ReplaceHistoryEntry`: Replace the current entry in the history stack. If `false`, append the new entry to the history stack. The default value is `false`. +* `HistoryEntryState`: Gets or sets the state to append to the history entry. + +```csharp +NavigationManager.NavigateTo("/path", new NavigationOptions +{ + HistoryEntryState = "Navigation state" +}); +``` + +For more information on the history stack, see the [Handle location changes in a component](#handle-location-changes-in-a-component) section. + ## Query strings Use the [`[SupplyParameterFromQuery]` attribute](xref:Microsoft.AspNetCore.Components.SupplyParameterFromQueryAttribute) with the [`[Parameter]` attribute](xref:Microsoft.AspNetCore.Components.ParameterAttribute) to specify that a component parameter of a routable component can come from the query string. @@ -2016,6 +1920,110 @@ In the following `App` component example: > [!NOTE] > Not throwing if the cancellation token in is canceled can result in unintended behavior, such as rendering a component from a previous navigation. +## Handle location changes in a component + +`RegisterLocationChangingHandler` registers a handler to process incoming navigation events. The handler's context (`LocationChangingContext`) provides the following properties: + +* `TargetLocation`: Gets the target location. +* `HistoryEntryState`: Gets the state associated with the target history entry. +* `IsNavigationIntercepted`: Gets whether the navigation was intercepted from a link. +* `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. +* `PreventNavigation`: Called to prevent the navigation from continuing. + +A component can register multiple location changing handlers, and calling always invokes all of the registered handlers. + +In the following example, a location changing handler is registered for navigation events. + +`Pages/NavHandler.razor`: + +```razor +@page "/nav-handler" +@inject NavigationManager NavigationManager + +

+ + +

+ +@code { + protected override void OnInitialized() + { + NavigationManager.RegisterLocationChangingHandler(OnLocationChanging); + } + + private async ValueTask OnLocationChanging(LocationChangingContext context) + { + if (context.TargetLocation == "counter") + { + context.PreventNavigation(); + } + } +} +``` + +Since internal navigation can be canceled asynchronously, multiple overlapping navigations may occur. For example, multiple handler calls may occur when the user rapidly selects the back button on a page or selects multiple links before a navigation is determined. The following is a summary of the asynchronous navigation logic: + +* If any location changing handlers are registered, all navigations are initially reverted, then replayed if the navigation isn't canceled. +* If overlapping navigation requests are made, the latest request always cancels earlier requests, which means the following: + * The app may treat multiple back and forward button selections as a single selection. + * If the user selects multiple links before the navigation completes, the last link selected determines the navigation. + +For more information on passing `NavigationOptions` to to control entries and state of the navigation history stack, see the [Navigation options](#navigation-options) section. + +For additional example code, see the [`NavigationManagerComponent` in the `BasicTestApp` (`dotnet/aspnetcore` reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/NavigationManagerComponent.razor). + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +To facilitate the control of navigation events in a Razor component, use the `NavigationLock` component. After the `NavigationLock` component is rendered, it intercepts navigation events. + +In the following `NavigationLockExample` component: + +* `ConfirmExternalNavigation` sets a browser dialog to prompt the user to either confirm or cancel the external navigation. The default value is `false`. An attempt to follow the link to Microsoft's website must be confirmed by the user before the navigation to `https://www.microsoft.com` succeeds. +* `OnBeforeInternalNavigation` sets a callback to be invoked when an internal navigation event occurs. `PreventNavigation` is called to prevent naviation from occurring if the user declines to confirm the navigation via a [JavaScript (JS) interop call](xref:blazor/js-interop/call-javascript-from-dotnet) that spawns the [JS `confirm` dialog](https://developer.mozilla.org/docs/Web/API/Window/confirm). + +`Pages/NavLock.razor`: + +```razor +@page "/nav-lock" +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager + + + +

+ +

+ +

+ Microsoft homepage +

+ +@code { + private void Navigate() + { + NavigationManager.NavigateTo("/"); + } + + private async Task OnBeforeInternalNavigation(LocationChangingContext context) + { + var isConfirmed = await JSRuntime.InvokeAsync("confirm", + "Are you sure you want to navigate to the Index page?"); + + if (!isConfirmed) + { + context.PreventNavigation(); + } + } +} +``` + +For additional example code, see the [`ConfigurableNavigationLock` component in the `BasicTestApp` (`dotnet/aspnetcore` reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/RouterTest/ConfigurableNavigationLock.razor). + ## `NavLink` and `NavMenu` components Use a component in place of HTML hyperlink elements (``) when creating navigation links. A component behaves like an `` element, except it toggles an `active` CSS class based on whether its `href` matches the current URL. The `active` class helps a user understand which page is the active page among the navigation links displayed. Optionally, assign a CSS class name to to apply a custom CSS class to the rendered link when the current route matches the `href`. From 6aa65973c0c19f089df0cf6802880827c33e3bad Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Sep 2022 07:22:21 -0500 Subject: [PATCH 03/17] Updates --- aspnetcore/blazor/fundamentals/routing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 3f89c66c4a3a..06cbbd48f7b4 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1583,7 +1583,7 @@ NavigationManager.NavigateTo("/path", new NavigationOptions }); ``` -For more information on the history stack, see the [Handle location changes in a component](#handle-location-changes-in-a-component) section. +For more information on obtaining the state associated with the target history entry while handling location changes, see the [Handle location changes in a component](#handle-location-changes-in-a-component) section. ## Query strings @@ -1922,7 +1922,7 @@ In the following `App` component example: ## Handle location changes in a component -`RegisterLocationChangingHandler` registers a handler to process incoming navigation events. The handler's context (`LocationChangingContext`) provides the following properties: +`RegisterLocationChangingHandler` registers a handler to process incoming navigation events. The handler's context provided by `LocationChangingContext` includes the following properties: * `TargetLocation`: Gets the target location. * `HistoryEntryState`: Gets the state associated with the target history entry. @@ -1930,7 +1930,7 @@ In the following `App` component example: * `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. * `PreventNavigation`: Called to prevent the navigation from continuing. -A component can register multiple location changing handlers, and calling always invokes all of the registered handlers. +A component can register multiple location changing handlers, and calling invokes all of the registered handlers. In the following example, a location changing handler is registered for navigation events. @@ -1965,7 +1965,7 @@ In the following example, a location changing handler is registered for navigati } ``` -Since internal navigation can be canceled asynchronously, multiple overlapping navigations may occur. For example, multiple handler calls may occur when the user rapidly selects the back button on a page or selects multiple links before a navigation is determined. The following is a summary of the asynchronous navigation logic: +Since internal navigation can be canceled asynchronously, multiple overlapping calls to registered handlers may occur. For example, multiple handler calls may occur when the user rapidly selects the back button on a page or selects multiple links before a navigation is executed. The following is a summary of the asynchronous navigation logic: * If any location changing handlers are registered, all navigations are initially reverted, then replayed if the navigation isn't canceled. * If overlapping navigation requests are made, the latest request always cancels earlier requests, which means the following: From 9346435b773a553a101f0297148353a6c284debd Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Sep 2022 07:38:52 -0500 Subject: [PATCH 04/17] Updates --- aspnetcore/blazor/fundamentals/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 06cbbd48f7b4..6d4408cedb86 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1980,7 +1980,7 @@ For additional example code, see the [`NavigationManagerComponent` in the `Basic To facilitate the control of navigation events in a Razor component, use the `NavigationLock` component. After the `NavigationLock` component is rendered, it intercepts navigation events. -In the following `NavigationLockExample` component: +In the following `NavLock` component: * `ConfirmExternalNavigation` sets a browser dialog to prompt the user to either confirm or cancel the external navigation. The default value is `false`. An attempt to follow the link to Microsoft's website must be confirmed by the user before the navigation to `https://www.microsoft.com` succeeds. * `OnBeforeInternalNavigation` sets a callback to be invoked when an internal navigation event occurs. `PreventNavigation` is called to prevent naviation from occurring if the user declines to confirm the navigation via a [JavaScript (JS) interop call](xref:blazor/js-interop/call-javascript-from-dotnet) that spawns the [JS `confirm` dialog](https://developer.mozilla.org/docs/Web/API/Window/confirm). From f0fa452c97e52e586bb98265d3edbb1f9dfd657b Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Sep 2022 07:46:20 -0500 Subject: [PATCH 05/17] Updates --- aspnetcore/blazor/fundamentals/routing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 6d4408cedb86..770d7c9938a4 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1930,7 +1930,7 @@ In the following `App` component example: * `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. * `PreventNavigation`: Called to prevent the navigation from continuing. -A component can register multiple location changing handlers, and calling invokes all of the registered handlers. +A component can register multiple location changing handlers, and calling invokes all of the registered handlers. Registered handlers are automatically disposed by the Navigation Manager when the component is disposed. In the following example, a location changing handler is registered for navigation events. @@ -1978,7 +1978,7 @@ For additional example code, see the [`NavigationManagerComponent` in the `Basic [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] -To facilitate the control of navigation events in a Razor component, use the `NavigationLock` component. After the `NavigationLock` component is rendered, it intercepts navigation events. +To facilitate the control of navigation events in a Razor component, use the `NavigationLock` component. After the `NavigationLock` component is rendered, it intercepts navigation events. Handlers associated with `NavigationLock` components are disposed automatically when the component is disposed. In the following `NavLock` component: From f0acf3f0e2a754685160ebe7e518ef6d2388d538 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Tue, 6 Sep 2022 13:05:25 -0500 Subject: [PATCH 06/17] Call out handler disposal and update example --- aspnetcore/blazor/fundamentals/routing.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 770d7c9938a4..75fd2324271c 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -1930,15 +1930,16 @@ In the following `App` component example: * `CancellationToken`: Gets a to determine if the navigation was canceled, for example, to determine if the user triggered a different navigation. * `PreventNavigation`: Called to prevent the navigation from continuing. -A component can register multiple location changing handlers, and calling invokes all of the registered handlers. Registered handlers are automatically disposed by the Navigation Manager when the component is disposed. +A component can register multiple location changing handlers, and calling invokes all of the registered handlers. [Dispose registered handlers](xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable) to avoid memory leaks. -In the following example, a location changing handler is registered for navigation events. +In the following example, a location changing handler is registered for navigation events. Note that the component implements `IDisposable` and disposes the handler in its `Dispose` method. `Pages/NavHandler.razor`: ```razor @page "/nav-handler" @inject NavigationManager NavigationManager +@implements IDisposable