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