diff --git a/packages/router/src/operators/activate_routes.ts b/packages/router/src/operators/activate_routes.ts index 3007441cb98..9489941cdd2 100644 --- a/packages/router/src/operators/activate_routes.ts +++ b/packages/router/src/operators/activate_routes.ts @@ -89,7 +89,9 @@ export class ActivateRoutes { private deactivateRouteAndItsChildren( route: TreeNode, parentContexts: ChildrenOutletContexts): void { - if (this.routeReuseStrategy.shouldDetach(route.value.snapshot)) { + // If there is no component, the Route is never attached to an outlet (because there is no + // component to attach). + if (route.value.component && this.routeReuseStrategy.shouldDetach(route.value.snapshot)) { this.detachAndStoreRouteSubtree(route, parentContexts); } else { this.deactivateRouteAndOutlet(route, parentContexts); diff --git a/packages/router/test/integration.spec.ts b/packages/router/test/integration.spec.ts index 4b03ab00f3f..e132c47048f 100644 --- a/packages/router/test/integration.spec.ts +++ b/packages/router/test/integration.spec.ts @@ -6083,6 +6083,57 @@ describe('Integration', () => { advance(fixture); expect(createdComps).toEqual(['parent', 'child', 'child']); })); + + it('should not try to detach the outlet of a route that does not get to attach a component', + fakeAsync(() => { + @Component({selector: 'root', template: ``}) + class Root { + } + + @Component({selector: 'component-a', template: 'Component A'}) + class ComponentA { + } + + @Component({selector: 'component-b', template: 'Component B'}) + class ComponentB { + } + + @NgModule({ + declarations: [ComponentA], + imports: [RouterModule.forChild([{path: '', component: ComponentA}])], + }) + class LoadedModule { + } + + @NgModule({ + declarations: [Root, ComponentB], + imports: [RouterTestingModule.withRoutes([ + {path: 'a', loadChildren: () => LoadedModule}, {path: 'b', component: ComponentB} + ])], + providers: [ + {provide: RouteReuseStrategy, useClass: AttachDetachReuseStrategy}, + ] + }) + class TestModule { + } + + TestBed.configureTestingModule({imports: [TestModule]}); + + const router = TestBed.inject(Router); + const strategy = TestBed.inject(RouteReuseStrategy); + const fixture = createRoot(router, Root); + + spyOn(strategy, 'shouldDetach').and.callThrough(); + + router.navigateByUrl('/a'); + advance(fixture); + + // Deactivate 'a' + // 'shouldDetach' should not be called for the componentless route + router.navigateByUrl('/b'); + advance(fixture); + expect(strategy.shouldDetach).toHaveBeenCalledTimes(1); + })); }); });