feat(router): allow redirect from guards by returning UrlTree (#26521)
* Improve type checking within the CheckGuards function * Change public API to allow returning of UrlTree instead of boolean PR Close #26521
This commit is contained in:
parent
17586f1e94
commit
152ca66eba
|
@ -11,10 +11,12 @@ import {EmptyError, Observable, Observer, from, of } from 'rxjs';
|
|||
import {catchError, concatAll, first, map, mergeMap} from 'rxjs/operators';
|
||||
|
||||
import {LoadedRouterConfig, Route, Routes} from './config';
|
||||
import {CanLoadFn} from './interfaces';
|
||||
import {RouterConfigLoader} from './router_config_loader';
|
||||
import {PRIMARY_OUTLET, Params, defaultUrlMatcher, navigationCancelingError} from './shared';
|
||||
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
|
||||
import {andObservables, forEach, waitForMap, wrapIntoObservable} from './utils/collection';
|
||||
import {isCanLoad, isFunction} from './utils/type_guards';
|
||||
|
||||
class NoMatch {
|
||||
public segmentGroup: UrlSegmentGroup|null;
|
||||
|
@ -408,8 +410,15 @@ function runCanLoadGuard(
|
|||
|
||||
const obs = from(canLoad).pipe(map((injectionToken: any) => {
|
||||
const guard = moduleInjector.get(injectionToken);
|
||||
return wrapIntoObservable(
|
||||
guard.canLoad ? guard.canLoad(route, segments) : guard(route, segments));
|
||||
let guardVal;
|
||||
if (isCanLoad(guard)) {
|
||||
guardVal = guard.canLoad(route, segments);
|
||||
} else if (isFunction<CanLoadFn>(guard)) {
|
||||
guardVal = guard(route, segments);
|
||||
} else {
|
||||
throw new Error('Invalid CanLoad guard');
|
||||
}
|
||||
return wrapIntoObservable(guardVal);
|
||||
}));
|
||||
|
||||
return andObservables(obs);
|
||||
|
|
|
@ -84,7 +84,7 @@ export interface CanActivate {
|
|||
}
|
||||
|
||||
export type CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) =>
|
||||
Observable<boolean>| Promise<boolean>| boolean | UrlTree;
|
||||
Observable<boolean|UrlTree>| Promise<boolean|UrlTree>| boolean | UrlTree;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
@ -246,7 +246,8 @@ export interface CanDeactivate<T> {
|
|||
|
||||
export type CanDeactivateFn<T> =
|
||||
(component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot,
|
||||
nextState?: RouterStateSnapshot) => Observable<boolean>| Promise<boolean>| boolean | UrlTree;
|
||||
nextState?: RouterStateSnapshot) =>
|
||||
Observable<boolean|UrlTree>| Promise<boolean|UrlTree>| boolean | UrlTree;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
|
|
@ -11,10 +11,13 @@ import {MonoTypeOperatorFunction, Observable, from, of } from 'rxjs';
|
|||
import {concatMap, every, first, map, mergeMap} from 'rxjs/operators';
|
||||
|
||||
import {ActivationStart, ChildActivationStart, Event} from '../events';
|
||||
import {CanActivateChildFn, CanActivateFn, CanDeactivateFn} from '../interfaces';
|
||||
import {NavigationTransition} from '../router';
|
||||
import {ActivatedRouteSnapshot, RouterStateSnapshot} from '../router_state';
|
||||
import {UrlTree} from '../url_tree';
|
||||
import {andObservables, wrapIntoObservable} from '../utils/collection';
|
||||
import {CanActivate, CanDeactivate, getCanActivateChild, getToken} from '../utils/preactivation';
|
||||
import {isCanActivate, isCanActivateChild, isCanDeactivate, isFunction} from '../utils/type_guards';
|
||||
|
||||
export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event) => void):
|
||||
MonoTypeOperatorFunction<NavigationTransition> {
|
||||
|
@ -29,7 +32,7 @@ export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event
|
|||
return runCanDeactivateChecks(
|
||||
canDeactivateChecks, targetSnapshot !, currentSnapshot, moduleInjector)
|
||||
.pipe(
|
||||
mergeMap((canDeactivate: boolean) => {
|
||||
mergeMap(canDeactivate => {
|
||||
return canDeactivate ?
|
||||
runCanActivateChecks(
|
||||
targetSnapshot !, canActivateChecks, moduleInjector, forwardEvent) :
|
||||
|
@ -42,12 +45,12 @@ export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event
|
|||
|
||||
function runCanDeactivateChecks(
|
||||
checks: CanDeactivate[], futureRSS: RouterStateSnapshot, currRSS: RouterStateSnapshot,
|
||||
moduleInjector: Injector): Observable<boolean> {
|
||||
moduleInjector: Injector): Observable<boolean|UrlTree> {
|
||||
return from(checks).pipe(
|
||||
mergeMap(
|
||||
(check: CanDeactivate) =>
|
||||
runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector)),
|
||||
every((result: boolean) => result === true));
|
||||
mergeMap(check => {
|
||||
return runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector);
|
||||
}),
|
||||
every(result => result === true));
|
||||
}
|
||||
|
||||
function runCanActivateChecks(
|
||||
|
@ -104,11 +107,13 @@ function runCanActivate(
|
|||
if (!canActivate || canActivate.length === 0) return of (true);
|
||||
const obs = from(canActivate).pipe(map((c: any) => {
|
||||
const guard = getToken(c, futureARS, moduleInjector);
|
||||
let observable: Observable<boolean>;
|
||||
if (guard.canActivate) {
|
||||
let observable;
|
||||
if (isCanActivate(guard)) {
|
||||
observable = wrapIntoObservable(guard.canActivate(futureARS, futureRSS));
|
||||
} else {
|
||||
} else if (isFunction<CanActivateFn>(guard)) {
|
||||
observable = wrapIntoObservable(guard(futureARS, futureRSS));
|
||||
} else {
|
||||
throw new Error('Invalid canActivate guard');
|
||||
}
|
||||
return observable.pipe(first());
|
||||
}));
|
||||
|
@ -128,11 +133,13 @@ function runCanActivateChild(
|
|||
return andObservables(from(canActivateChildGuards).pipe(map((d: any) => {
|
||||
const obs = from(d.guards).pipe(map((c: any) => {
|
||||
const guard = getToken(c, d.node, moduleInjector);
|
||||
let observable: Observable<boolean>;
|
||||
if (guard.canActivateChild) {
|
||||
let observable;
|
||||
if (isCanActivateChild(guard)) {
|
||||
observable = wrapIntoObservable(guard.canActivateChild(futureARS, futureRSS));
|
||||
} else {
|
||||
} else if (isFunction<CanActivateChildFn>(guard)) {
|
||||
observable = wrapIntoObservable(guard(futureARS, futureRSS));
|
||||
} else {
|
||||
throw new Error('Invalid CanActivateChild guard');
|
||||
}
|
||||
return observable.pipe(first());
|
||||
}));
|
||||
|
@ -141,17 +148,20 @@ function runCanActivateChild(
|
|||
}
|
||||
|
||||
function runCanDeactivate(
|
||||
component: Object | null, currARS: ActivatedRouteSnapshot, currRSS: RouterStateSnapshot,
|
||||
futureRSS: RouterStateSnapshot, moduleInjector: Injector): Observable<boolean> {
|
||||
component: Object| null, currARS: ActivatedRouteSnapshot, currRSS: RouterStateSnapshot,
|
||||
futureRSS: RouterStateSnapshot, moduleInjector: Injector): Observable<boolean|UrlTree> {
|
||||
const canDeactivate = currARS && currARS.routeConfig ? currARS.routeConfig.canDeactivate : null;
|
||||
if (!canDeactivate || canDeactivate.length === 0) return of (true);
|
||||
const canDeactivate$ = from(canDeactivate).pipe(mergeMap((c: any) => {
|
||||
const guard = getToken(c, currARS, moduleInjector);
|
||||
let observable: Observable<boolean>;
|
||||
if (guard.canDeactivate) {
|
||||
observable = wrapIntoObservable(guard.canDeactivate(component, currARS, currRSS, futureRSS));
|
||||
} else {
|
||||
let observable;
|
||||
if (isCanDeactivate(guard)) {
|
||||
observable =
|
||||
wrapIntoObservable(guard.canDeactivate(component !, currARS, currRSS, futureRSS));
|
||||
} else if (isFunction<CanDeactivateFn<any>>(guard)) {
|
||||
observable = wrapIntoObservable(guard(component, currARS, currRSS, futureRSS));
|
||||
} else {
|
||||
throw new Error('Invalid CanDeactivate guard');
|
||||
}
|
||||
return observable.pipe(first());
|
||||
}));
|
||||
|
|
|
@ -38,6 +38,6 @@ export function isCanActivateChild(guard: any): guard is CanActivateChild {
|
|||
return guard && isFunction<CanActivateChild>(guard.canActivate);
|
||||
}
|
||||
|
||||
export function isCanDeactivate(guard: any): guard is CanDeactivate<Type<any>> {
|
||||
return guard && isFunction<CanDeactivate<Type<any>>>(guard.canDeactivate);
|
||||
export function isCanDeactivate<T>(guard: any): guard is CanDeactivate<T> {
|
||||
return guard && isFunction<CanDeactivate<T>>(guard.canDeactivate);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue