diff --git a/modules/@angular/router/src/apply_redirects.ts b/modules/@angular/router/src/apply_redirects.ts index ed5e0974bb..82717104ef 100644 --- a/modules/@angular/router/src/apply_redirects.ts +++ b/modules/@angular/router/src/apply_redirects.ts @@ -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 { function canLoadFails(route: Route): Observable { return new Observable( - (obs: Observer) => obs.error(new Error( + (obs: Observer) => obs.error(new NavigationCancelingError( `Cannot load children because the guard of the route "path: '${route.path}'" returned false`))); } diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 1347cfb879..61952228fb 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -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 { 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 => { + 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; - this.routerEvents.next(new NavigationError(id, this.serializeUrl(url), e)); - rejectPromise(e); }); }); } diff --git a/modules/@angular/router/src/shared.ts b/modules/@angular/router/src/shared.ts index 111e45c337..06e7526a89 100644 --- a/modules/@angular/router/src/shared.ts +++ b/modules/@angular/router/src/shared.ts @@ -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 = (new Error(message)).stack; + } + toString(): string { return this.message; } +} \ No newline at end of file diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index 707cab20cc..243e725fbd 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -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); diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index daf6c4ab15..fba3dbf81b 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -84,8 +84,9 @@ export declare type LoadChildrenCallback = () => Type | Promise> /** @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; }