fix(router): canLoad should cancel a navigation instead of failing it (#11001)
This commit is contained in:
		
							parent
							
								
									7dfcaac730
								
							
						
					
					
						commit
						f1ce7607a6
					
				| @ -19,7 +19,7 @@ import {EmptyError} from 'rxjs/util/EmptyError'; | ||||
| 
 | ||||
| import {Route, Routes} from './config'; | ||||
| import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader'; | ||||
| import {PRIMARY_OUTLET} from './shared'; | ||||
| import {NavigationCancelingError, PRIMARY_OUTLET} from './shared'; | ||||
| import {UrlSegment, UrlSegmentGroup, UrlTree} from './url_tree'; | ||||
| import {andObservables, merge, waitForMap, wrapIntoObservable} from './utils/collection'; | ||||
| 
 | ||||
| @ -43,7 +43,7 @@ function absoluteRedirect(segments: UrlSegment[]): Observable<UrlSegmentGroup> { | ||||
| 
 | ||||
| function canLoadFails(route: Route): Observable<LoadedRouterConfig> { | ||||
|   return new Observable<LoadedRouterConfig>( | ||||
|       (obs: Observer<LoadedRouterConfig>) => obs.error(new Error( | ||||
|       (obs: Observer<LoadedRouterConfig>) => obs.error(new NavigationCancelingError( | ||||
|           `Cannot load children because the guard of the route "path: '${route.path}'" returned false`))); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -29,7 +29,7 @@ import {recognize} from './recognize'; | ||||
| import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader'; | ||||
| import {RouterOutletMap} from './router_outlet_map'; | ||||
| import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from './router_state'; | ||||
| import {PRIMARY_OUTLET, Params} from './shared'; | ||||
| import {NavigationCancelingError, PRIMARY_OUTLET, Params} from './shared'; | ||||
| import {UrlSerializer, UrlTree, containsTree, createEmptyUrlTree} from './url_tree'; | ||||
| import {andObservables, forEach, merge, shallowEqual, waitForMap, wrapIntoObservable} from './utils/collection'; | ||||
| import {TreeNode} from './utils/tree'; | ||||
| @ -162,7 +162,7 @@ export class NavigationEnd { | ||||
|  * @stable | ||||
|  */ | ||||
| export class NavigationCancel { | ||||
|   constructor(public id: number, public url: string) {} | ||||
|   constructor(public id: number, public url: string, public reason: string) {} | ||||
| 
 | ||||
|   toString(): string { return `NavigationCancel(id: ${this.id}, url: '${this.url}')`; } | ||||
| } | ||||
| @ -440,7 +440,9 @@ export class Router { | ||||
|       id: number): Promise<boolean> { | ||||
|     if (id !== this.navigationId) { | ||||
|       this.location.go(this.urlSerializer.serialize(this.currentUrlTree)); | ||||
|       this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url))); | ||||
|       this.routerEvents.next(new NavigationCancel( | ||||
|           id, this.serializeUrl(url), | ||||
|           `Navigation ID ${id} is not equal to the current navigation id ${this.navigationId}`)); | ||||
|       return Promise.resolve(false); | ||||
|     } | ||||
| 
 | ||||
| @ -518,15 +520,22 @@ export class Router { | ||||
|                       new NavigationEnd(id, this.serializeUrl(url), this.serializeUrl(appliedUrl))); | ||||
|                   resolvePromise(true); | ||||
|                 } else { | ||||
|                   this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url))); | ||||
|                   this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url), '')); | ||||
|                   resolvePromise(false); | ||||
|                 } | ||||
|               }, | ||||
|               e => { | ||||
|                 this.currentRouterState = storedState; | ||||
|                 this.currentUrlTree = storedUrl; | ||||
|                 if (e instanceof NavigationCancelingError) { | ||||
|                   this.navigated = true; | ||||
|                   this.routerEvents.next( | ||||
|                       new NavigationCancel(id, this.serializeUrl(url), e.message)); | ||||
|                   resolvePromise(false); | ||||
|                 } else { | ||||
|                   this.routerEvents.next(new NavigationError(id, this.serializeUrl(url), e)); | ||||
|                   rejectPromise(e); | ||||
|                 } | ||||
|                 this.currentRouterState = storedState; | ||||
|                 this.currentUrlTree = storedUrl; | ||||
|               }); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @ -22,3 +22,12 @@ export const PRIMARY_OUTLET = 'primary'; | ||||
| export type Params = { | ||||
|   [key: string]: any | ||||
| }; | ||||
| 
 | ||||
| export class NavigationCancelingError extends Error { | ||||
|   public stack: any; | ||||
|   constructor(public message: string) { | ||||
|     super(message); | ||||
|     this.stack = (<any>new Error(message)).stack; | ||||
|   } | ||||
|   toString(): string { return this.message; } | ||||
| } | ||||
| @ -1230,13 +1230,14 @@ describe('Integration', () => { | ||||
| 
 | ||||
| 
 | ||||
|                  // failed navigation
 | ||||
|                  router.navigateByUrl('/lazyFalse/loaded').catch(s => {}); | ||||
|                  router.navigateByUrl('/lazyFalse/loaded'); | ||||
|                  advance(fixture); | ||||
| 
 | ||||
|                  expect(location.path()).toEqual('/'); | ||||
| 
 | ||||
|                  expectEvents(recordedEvents, [ | ||||
|                    [NavigationStart, '/lazyFalse/loaded'], [NavigationError, '/lazyFalse/loaded'] | ||||
|                    [NavigationStart, '/lazyFalse/loaded'], | ||||
|                    [NavigationCancel, '/lazyFalse/loaded'] | ||||
|                  ]); | ||||
| 
 | ||||
|                  recordedEvents.splice(0); | ||||
|  | ||||
							
								
								
									
										3
									
								
								tools/public_api_guard/router/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								tools/public_api_guard/router/index.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -84,8 +84,9 @@ export declare type LoadChildrenCallback = () => Type<any> | Promise<Type<any>> | ||||
| /** @stable */ | ||||
| export declare class NavigationCancel { | ||||
|     id: number; | ||||
|     reason: string; | ||||
|     url: string; | ||||
|     constructor(id: number, url: string); | ||||
|     constructor(id: number, url: string, reason: string); | ||||
|     toString(): string; | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user