From d509ee078b24cdce2a3fa3fe906f4c6b60bec777 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 2 Nov 2016 11:30:00 -0700 Subject: [PATCH] fix(router): reset URL to the stable state when a navigation gets canceled Closes #10321 --- modules/@angular/router/src/router.ts | 19 +++++++----- .../@angular/router/test/integration.spec.ts | 29 +++++++++++++++++++ tools/public_api_guard/router/index.d.ts | 2 +- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index b45dcc2ad8..939344de4a 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -702,7 +702,7 @@ export class Router { this.currentRouterState = state; if (!shouldPreventPushState) { - let path = this.urlSerializer.serialize(this.rawUrlTree); + const path = this.urlSerializer.serialize(this.rawUrlTree); if (this.location.isCurrentPathEqualTo(path) || shouldReplaceUrl) { this.location.replaceState(path); } else { @@ -722,12 +722,14 @@ export class Router { new NavigationEnd(id, this.serializeUrl(url), this.serializeUrl(appliedUrl))); resolvePromise(true); } else { + this.resetUrlToCurrentUrlTree(); this.routerEvents.next(new NavigationCancel(id, this.serializeUrl(url), '')); resolvePromise(false); } }, (e: any) => { if (e instanceof NavigationCancelingError) { + this.resetUrlToCurrentUrlTree(); this.navigated = true; this.routerEvents.next( new NavigationCancel(id, this.serializeUrl(url), e.message)); @@ -741,15 +743,18 @@ export class Router { } } - if (id === this.navigationId) { - this.currentRouterState = storedState; - this.currentUrlTree = storedUrl; - this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl); - this.location.replaceState(this.serializeUrl(this.rawUrlTree)); - } + this.currentRouterState = storedState; + this.currentUrlTree = storedUrl; + this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl); + this.location.replaceState(this.serializeUrl(this.rawUrlTree)); }); }); } + + private resetUrlToCurrentUrlTree(): void { + const path = this.urlSerializer.serialize(this.rawUrlTree); + this.location.replaceState(path); + } } diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index 9f5fbea00c..342ae157fa 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -1213,6 +1213,35 @@ describe('Integration', () => { expect(location.path()).toEqual('/team/22'); }))); }); + + describe('should reset the location when cancleling a navigation', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: 'alwaysFalse', + useValue: (a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { return false; } + }] + }); + }); + + it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { + const fixture = createRoot(router, RootCmp); + + router.resetConfig([ + {path: 'one', component: SimpleCmp}, + {path: 'two', component: SimpleCmp, canActivate: ['alwaysFalse']} + ]); + + router.navigateByUrl('/one'); + advance(fixture); + expect(location.path()).toEqual('/one'); + + location.go('/two'); + advance(fixture); + expect(location.path()).toEqual('/one'); + + }))); + }); }); describe('CanDeactivate', () => { diff --git a/tools/public_api_guard/router/index.d.ts b/tools/public_api_guard/router/index.d.ts index 6da66d6bae..f9f6643c81 100644 --- a/tools/public_api_guard/router/index.d.ts +++ b/tools/public_api_guard/router/index.d.ts @@ -228,7 +228,7 @@ export declare class RouterLink { routerLink: any[] | string; urlTree: UrlTree; constructor(router: Router, route: ActivatedRoute, locationStrategy: LocationStrategy); - onClick(button: number, ctrlKey: boolean, metaKey: boolean): boolean; + onClick(): boolean; } /** @stable */