diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 60ff79515a..50b4b9705f 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -921,9 +921,7 @@ class ActivateRoutes { this.activateRoutes(c, prevChildren[c.value.outlet], outletMap); delete prevChildren[c.value.outlet]; }); - forEach( - prevChildren, - (v: any, k: string) => this.deactivateOutletAndItChildren(outletMap._outlets[k])); + forEach(prevChildren, (v: any, k: string) => this.deactiveRouteAndItsChildren(v, outletMap)); } activateRoutes( @@ -939,7 +937,7 @@ class ActivateRoutes { // If we have a normal route, we need to go through an outlet. if (future.component) { - const outlet = getOutlet(parentOutletMap, futureNode.value); + const outlet = getOutlet(parentOutletMap, future); this.activateChildRoutes(futureNode, currNode, outlet.outletMap); // if we have a componentless route, we recurse but keep the same outlet map. @@ -948,15 +946,7 @@ class ActivateRoutes { } } else { if (curr) { - // if we had a normal route, we need to deactivate only that outlet. - if (curr.component) { - const outlet = getOutlet(parentOutletMap, futureNode.value); - this.deactivateOutletAndItChildren(outlet); - - // if we had a componentless route, we need to deactivate everything! - } else { - this.deactivateOutletMap(parentOutletMap); - } + this.deactiveRouteAndItsChildren(currNode, parentOutletMap); } // if we have a normal route, we need to advance the route @@ -998,16 +988,32 @@ class ActivateRoutes { outletMap); } - private deactivateOutletAndItChildren(outlet: RouterOutlet): void { + private deactiveRouteAndItsChildren( + route: TreeNode, parentOutletMap: RouterOutletMap): void { + const prevChildren: {[key: string]: any} = nodeChildrenAsMap(route); + let outlet: RouterOutlet = null; + + // getOutlet throws when cannot find the right outlet, + // which can happen if an outlet was in an NgIf and was removed + try { + outlet = getOutlet(parentOutletMap, route.value); + } catch (e) { + return; + } + const childOutletMap = outlet.outletMap; + + forEach(prevChildren, (v: any, k: string) => { + if (route.value.component) { + this.deactiveRouteAndItsChildren(v, childOutletMap); + } else { + this.deactiveRouteAndItsChildren(v, parentOutletMap); + } + }); + if (outlet && outlet.isActivated) { - this.deactivateOutletMap(outlet.outletMap); outlet.deactivate(); } } - - private deactivateOutletMap(outletMap: RouterOutletMap): void { - forEach(outletMap._outlets, (v: RouterOutlet) => this.deactivateOutletAndItChildren(v)); - } } function parentLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig { diff --git a/modules/@angular/router/test/integration.spec.ts b/modules/@angular/router/test/integration.spec.ts index 7a27827cc3..0ee0a79cb4 100644 --- a/modules/@angular/router/test/integration.spec.ts +++ b/modules/@angular/router/test/integration.spec.ts @@ -531,6 +531,29 @@ describe('Integration', () => { expect(fixture.nativeElement).toHaveText('primary [simple] right [user victor]'); }))); + it('should not deactivate aux routes when navigating from a componentless routes', + fakeAsync(inject( + [Router, Location, NgModuleFactoryLoader], + (router: Router, location: Location, loader: SpyNgModuleFactoryLoader) => { + const fixture = createRoot(router, TwoOutletsCmp); + + router.resetConfig([ + {path: 'simple', component: SimpleCmp}, + {path: 'componentless', children: [{path: 'simple', component: SimpleCmp}]}, + {path: 'user/:name', outlet: 'aux', component: UserCmp} + ]); + + router.navigateByUrl('/componentless/simple(aux:user/victor)'); + advance(fixture); + expect(location.path()).toEqual('/componentless/simple(aux:user/victor)'); + expect(fixture.nativeElement).toHaveText('[ simple, aux: user victor ]'); + + router.navigateByUrl('/simple(aux:user/victor)'); + advance(fixture); + expect(location.path()).toEqual('/simple(aux:user/victor)'); + expect(fixture.nativeElement).toHaveText('[ simple, aux: user victor ]'); + }))); + it('should emit an event when an outlet gets activated', fakeAsync(() => { @Component({ selector: 'container',