diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 31b11b02bc..60ff79515a 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -764,7 +764,7 @@ export class PreActivation { }); forEach( prevChildren, - (v: any, k: string) => this.deactivateOutletAndItChildren(v, outletMap._outlets[k])); + (v: any, k: string) => this.deactiveRouteAndItsChildren(v, outletMap._outlets[k])); } traverseRoutes( @@ -794,14 +794,7 @@ export class PreActivation { } } else { if (curr) { - // if we had a normal route, we need to deactivate only that outlet. - if (curr.component) { - this.deactivateOutletAndItChildren(curr, outlet); - - // if we had a componentless route, we need to deactivate everything! - } else { - this.deactivateOutletMap(parentOutletMap); - } + this.deactiveRouteAndItsChildren(currNode, outlet); } this.checks.push(new CanActivate(futurePath)); @@ -816,19 +809,17 @@ export class PreActivation { } } - private deactivateOutletAndItChildren(route: ActivatedRouteSnapshot, outlet: RouterOutlet): void { - if (outlet && outlet.isActivated) { - this.deactivateOutletMap(outlet.outletMap); - this.checks.push(new CanDeactivate(outlet.component, route)); - } - } + private deactiveRouteAndItsChildren( + route: TreeNode, outlet: RouterOutlet): void { + const prevChildren: {[key: string]: any} = nodeChildrenAsMap(route); - private deactivateOutletMap(outletMap: RouterOutletMap): void { - forEach(outletMap._outlets, (v: RouterOutlet) => { - if (v.isActivated) { - this.deactivateOutletAndItChildren(v.activatedRoute.snapshot, v); - } + forEach(prevChildren, (v: any, k: string) => { + const childOutlet = outlet ? outlet.outletMap._outlets[k] : null; + this.deactiveRouteAndItsChildren(v, childOutlet); }); + + const component = outlet && outlet.isActivated ? outlet.component : null; + this.checks.push(new CanDeactivate(component, route.value)); } private runCanActivate(future: ActivatedRouteSnapshot): Observable { diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index 0ddbd228c6..743ea475a4 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -1055,7 +1055,11 @@ describe('Integration', () => { describe('CanDeactivate', () => { describe('should not deactivate a route when CanDeactivate returns false', () => { + let log: any; + beforeEach(() => { + log = []; + TestBed.configureTestingModule({ providers: [ { @@ -1076,6 +1080,13 @@ describe('Integration', () => { return a.params['name'] === 'victor'; } }, + { + provide: 'RecordingDeactivate', + useValue: (c: any, a: ActivatedRouteSnapshot, b: RouterStateSnapshot) => { + log.push(['Deactivate', a.routeConfig.path]); + return true; + } + }, ] }); }); @@ -1103,27 +1114,37 @@ describe('Integration', () => { expect(canceledStatus).toEqual(false); }))); - it('works (componentless route)', + it('works with componentless routes', fakeAsync(inject([Router, Location], (router: Router, location: Location) => { const fixture = createRoot(router, RootCmp); - router.resetConfig([{ - path: 'parent/:id', - canDeactivate: ['CanDeactivateParent'], - children: [{path: 'simple', component: SimpleCmp}] - }]); + router.resetConfig([ + { + path: 'grandparent', + canDeactivate: ['RecordingDeactivate'], + children: [{ + path: 'parent', + canDeactivate: ['RecordingDeactivate'], + children: [{ + path: 'child', + canDeactivate: ['RecordingDeactivate'], + children: [{path: 'simple', component: SimpleCmp}] + }] + }] + }, + {path: 'simple', component: SimpleCmp} + ]); - router.navigateByUrl('/parent/22/simple'); + router.navigateByUrl('/grandparent/parent/child/simple'); advance(fixture); - expect(location.path()).toEqual('/parent/22/simple'); + expect(location.path()).toEqual('/grandparent/parent/child/simple'); - router.navigateByUrl('/parent/33/simple'); + router.navigateByUrl('/simple'); advance(fixture); - expect(location.path()).toEqual('/parent/33/simple'); - router.navigateByUrl('/parent/44/simple'); - advance(fixture); - expect(location.path()).toEqual('/parent/33/simple'); + expect(log).toEqual([ + ['Deactivate', 'child'], ['Deactivate', 'parent'], ['Deactivate', 'grandparent'] + ]); }))); it('works with a nested route',