The solution to this is somewhere in Angular <router-outlet>
displays template twice, but I'm unable to apply it because my example differs due to dynamically loaded modules.
I have a nested <router-outlet>
setup;
localhost:4200/login
renders the LoginComponent
in the top-level component, AppComponent
localhost:4200/main
renders the MainComponent
in the top-level component, AppComponent
MainComponent
contains a <router-outlet>
in which LeafComponent
is rendered.The app-structure:
app.component.ts <router-outlet>
components/
login/
login-component.ts
main/
main-module.ts
main-component.ts <router-outlet>
components/
leaf-component.ts
The following is taken from app.module
and works iff main-module
is not dynamically loaded. I.e. it only works if main-component
is instantiated.
const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['/login']);
const redirectLoggedInToMain = () => redirectLoggedInTo(['/main']);
const appRoutes: Routes = [
{
path: 'login',
pathMatch: 'full',
component: LoginComponent,
canActivate: [AngularFireAuthGuard],
data: { authGuardPipe: redirectLoggedInToMain },
},
{
path: '',
component: MainComponent,
canActivate: [AngularFireAuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
children: [
{ path: '', redirectTo: '/main', pathMatch: 'full' },
// ----> Render leaf-component in main-component <router-outlet>
{
path: 'main',
pathMatch: 'full',
component: LeafComponent,
},
],
},
{ path: '**', component: PageNotFoundComponent },
];
This works. But when dynamically loading the LeafModule
, the MainComponent
is rendered twice. I.e. it does not work when main-module
is dynamically loaded and then main-component
is instantiated.
const appRoutes: Routes = [
{
path: 'login',
pathMatch: 'full',
component: LoginComponent,
canActivate: [AngularFireAuthGuard],
data: { authGuardPipe: redirectLoggedInToMain },
},
{
path: '',
component: MainComponent,
canActivate: [AngularFireAuthGuard],
data: { authGuardPipe: redirectUnauthorizedToLogin },
children: [
{ path: '', redirectTo: '/main', pathMatch: 'full' },
// ----> Dynamically load leaf-module
// ----> Render leaf-component in main-component <router-outlet>
{
path: 'main',
pathMatch: 'full',
loadChildren: () => import('./components/main/components/leaf/leaf.module').then(m => m.LeafModule)
},
],
},
{ path: '**', component: PageNotFoundComponent },
];
Solution Details
@inge-olaisen's suggestion solved my problem but I wanted to add some detail here for completeness.
My mistake was not appreciating that a dynamically loaded module MUST(?) have a route, even if that route just loads the default modulel:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LeafComponent } from './leaf.component';
const routes: Routes = [
{
path: '',
component: LeafComponent,
pathMatch: 'full',
},
];
@NgModule({
imports: [
RouterModule.forChild(routes),
],
exports: [RouterModule]
})
export class LeafRoutingModule { }
This still seems a little redundant since because there are no routes. But because I want to lazy load the component, I must add them.
I also used separate routing modules which did make things clearer.
Finally, Angular 8 | Nested Routing with Multiple RouterOutlet using loadChildren having own Router Modules Example Application, is probably the simplest and most complete example of nested routing with lazy-loaded modules out there (IMO). It also uses a recent version of Angular and thus the new load loadChildren: () => import('./foo.module').then(m => m.FooModule)
syntax. It also has a StackBlitz demo.
We're missing some key information. Does the leaf.module have it's own router module? A leaf-routing.module.ts for instance. That routing module should in turn have pathmatching for '' which loads the LeafComponent.
If you don't have that routing module, the module doesn't know which route to look for and I'm assuming the problem is the same as the person you linked to had, but instead you're missing the '' path for the LeafRoutingModule.