mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
feat(router): Allow resolvers to return RedirectCommand (#54556)
Returning a `RedirectCommand` from a resolver can be interpreted as distinctly different from regular resolved data. When a resolver returns `RedirectCommand` we can interperet this as an intention to redirect in the same way as other guards. resolves #29089 PR Close #54556
This commit is contained in:
parent
8a8181a54d
commit
87f3f27f90
4 changed files with 34 additions and 3 deletions
|
|
@ -636,7 +636,7 @@ export class ResolveEnd extends RouterEvent {
|
|||
}
|
||||
|
||||
// @public
|
||||
export type ResolveFn<T> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => MaybeAsync<T>;
|
||||
export type ResolveFn<T> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => MaybeAsync<T | RedirectCommand>;
|
||||
|
||||
// @public
|
||||
export class ResolveStart extends RouterEvent {
|
||||
|
|
|
|||
|
|
@ -165,6 +165,11 @@ export type Data = {
|
|||
*
|
||||
* Represents the resolved data associated with a particular route.
|
||||
*
|
||||
* Returning a `RedirectCommand` directs the router to cancel the current navigation and redirect to
|
||||
* the location provided in the `RedirectCommand`. Note that there are no ordering guarantees when
|
||||
* resolvers execute. If multiple resolvers would return a `RedirectCommand`, only the first one
|
||||
* returned will be used.
|
||||
*
|
||||
* @see {@link Route#resolve}
|
||||
*
|
||||
* @publicApi
|
||||
|
|
@ -1221,7 +1226,7 @@ export interface Resolve<T> {
|
|||
export type ResolveFn<T> = (
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot,
|
||||
) => MaybeAsync<T>;
|
||||
) => MaybeAsync<T | RedirectCommand>;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {EnvironmentInjector, ProviderToken, runInInjectionContext} from '@angula
|
|||
import {EMPTY, from, MonoTypeOperatorFunction, Observable, of, throwError} from 'rxjs';
|
||||
import {catchError, concatMap, first, map, mapTo, mergeMap, takeLast, tap} from 'rxjs/operators';
|
||||
|
||||
import {ResolveData} from '../models';
|
||||
import {RedirectCommand, ResolveData} from '../models';
|
||||
import {NavigationTransition} from '../navigation_transition';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
|
|
@ -23,6 +23,8 @@ import {getDataKeys, wrapIntoObservable} from '../utils/collection';
|
|||
import {getClosestRouteInjector} from '../utils/config';
|
||||
import {getTokenOrFunctionIdentity} from '../utils/preactivation';
|
||||
import {isEmptyError} from '../utils/type_guards';
|
||||
import {redirectingNavigationError} from '../navigation_canceling_error';
|
||||
import {DefaultUrlSerializer} from '../url_tree';
|
||||
|
||||
export function resolveData(
|
||||
paramsInheritanceStrategy: 'emptyOnly' | 'always',
|
||||
|
|
@ -112,6 +114,9 @@ function resolveNode(
|
|||
getResolver(resolve[key], futureARS, futureRSS, injector).pipe(
|
||||
first(),
|
||||
tap((value: any) => {
|
||||
if (value instanceof RedirectCommand) {
|
||||
throw redirectingNavigationError(new DefaultUrlSerializer(), value);
|
||||
}
|
||||
data[key] = value;
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2432,6 +2432,27 @@ for (const browserAPI of ['navigation', 'history'] as const) {
|
|||
}),
|
||||
));
|
||||
|
||||
it('should redirect if a resolver returns RedirectCommand', fakeAsync(() => {
|
||||
const router = TestBed.inject(Router);
|
||||
const fixture = createRoot(router, RootCmpWithTwoOutlets);
|
||||
|
||||
router.resetConfig([
|
||||
{
|
||||
path: 'parent/:id',
|
||||
component: BlankCmp,
|
||||
resolve: {redirectMe: () => new RedirectCommand(router.parseUrl('/login'))},
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
component: BlankCmp,
|
||||
},
|
||||
]);
|
||||
|
||||
router.navigateByUrl('/parent/1');
|
||||
advance(fixture);
|
||||
expect(router.url).toEqual('/login');
|
||||
}));
|
||||
|
||||
it('should handle errors', fakeAsync(
|
||||
inject([Router], (router: Router) => {
|
||||
const fixture = createRoot(router, RootCmp);
|
||||
|
|
|
|||
Loading…
Reference in a new issue