Files
angular__angular/packages/router/src/navigation_canceling_error.ts
Andrew Scott 8735af08b9 feat(router): Add ability to return UrlTree with NavigationBehaviorOptions from guards (#45023)
Returning `UrlTree` from a guard was a convenient new feature added to
the `Router`. However, it does not have feature-parity with the old
`router.navigate(...); return false;` pattern. The most common use-case
for this feature is to redirect to a new page _without_ updating the URL
from the initially attempted navigation. For example, rendering a 404
page when the user does not have access privelages to a route.

Fixes #17004
Fixes #27148

BREAKING CHANGE: Guards can now return `RedirectCommand` for redirects
in addition to `UrlTree`. Code which expects only `boolean` or `UrlTree`
values in `Route` types will need to be adjusted.

PR Close #45023
2024-03-12 09:19:14 -07:00

63 lines
2.2 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NavigationCancellationCode} from './events';
import {NavigationBehaviorOptions, RedirectCommand} from './models';
import {isUrlTree, UrlSerializer, UrlTree} from './url_tree';
export const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
export type NavigationCancelingError = Error & {
[NAVIGATION_CANCELING_ERROR]: true;
cancellationCode: NavigationCancellationCode;
};
export type RedirectingNavigationCancelingError = NavigationCancelingError & {
url: UrlTree;
navigationBehaviorOptions?: NavigationBehaviorOptions;
cancellationCode: NavigationCancellationCode.Redirect;
};
export function redirectingNavigationError(
urlSerializer: UrlSerializer,
redirect: UrlTree | RedirectCommand,
): RedirectingNavigationCancelingError {
const {redirectTo, navigationBehaviorOptions} = isUrlTree(redirect)
? {redirectTo: redirect, navigationBehaviorOptions: undefined}
: redirect;
const error = navigationCancelingError(
ngDevMode && `Redirecting to "${urlSerializer.serialize(redirectTo)}"`,
NavigationCancellationCode.Redirect,
) as RedirectingNavigationCancelingError;
error.url = redirectTo;
error.navigationBehaviorOptions = navigationBehaviorOptions;
return error;
}
export function navigationCancelingError(
message: string | null | false,
code: NavigationCancellationCode,
) {
const error = new Error(`NavigationCancelingError: ${message || ''}`) as NavigationCancelingError;
error[NAVIGATION_CANCELING_ERROR] = true;
error.cancellationCode = code;
return error;
}
export function isRedirectingNavigationCancelingError(
error: unknown | RedirectingNavigationCancelingError,
): error is RedirectingNavigationCancelingError {
return (
isNavigationCancelingError(error) &&
isUrlTree((error as RedirectingNavigationCancelingError).url)
);
}
export function isNavigationCancelingError(error: unknown): error is NavigationCancelingError {
return !!error && (error as NavigationCancelingError)[NAVIGATION_CANCELING_ERROR];
}