7

I'm using multiple named angular 8 router-outlet in a web app. All the routerLink seems to work as it changes the URL but components in my 2nd router-outlet are imported but not initialized nor rendered.


I made a Stackblitz available here : https://stackblitz.com/edit/ng-multiple-router-outlet?file=src/app/app.component.ts

As you can see, when you click on the sidebar, under photos you have a second navigation level by clicking on Google or Facebook but nothing is rendered.

In modules, components used in other modules and RouterModule are well exported to be accessible, I don't see what I've done wrong.

I tried to declare the routes with both forRoot and forChild methods, I put some logs, but I'm running out of clues.

Thanks for your help !

  • Look at the paths in your urls when you select Facebook and Google. – DetectivePikachu Aug 09 '19 at 13:37
  • Yes, it should render the right component in the right outlet no ? I'm new to Angular 8 but seems like it's the expected behavior from the doc and all articles I've read. For exemple : `(selection:facebook//sidebar:photos)` Sidebar => photos path Selection => facebook path – Adrien Martinet Aug 09 '19 at 13:52
  • @ĴošħWilliard have you more hint to give me please ? I'm still stuck – Adrien Martinet Aug 12 '19 at 07:16
  • Hi @ĴošħWilliard I changed the Stackblitz URL to https://stackblitz.com/edit/ng-multiple-router-outlet?file=src/app/app.component.ts Have you a hint to give me, I'm still stuck – Adrien Martinet Aug 16 '19 at 08:08

1 Answers1

6

Angular router is pretty simple once you understand how nested routes works there.

Let's imagine a simple configuration:

RouterModule.forRoot([
  { 
    path: '',
    component: HomeComponent,
    children: [
       { path: 'child', component: ChildComponent }
    ]
  }
])

How would you use router-outlet to cover all routes above?

app.component.html
     \
   contains
       \
    <router-outlet></router-outlet>
                 \/
             renders
                  \
              HomeComponent
             home.component.html
                  \
               contains
                    \
               <router-outlet></router-outlet>
                   renders
                       \
                     ChildComponent

The main takeaway here is that router-outlet renders component depending on router context. Once it renders component a new context is created and all router-outlet's declared at this level will look at children configuration.

The same is true for named routes.

You've generated the link like:

(selection:facebook//sidebar:photos)

It means that these named routes should be at the same root level. But you defined <router-outlet name="selection"></router-outlet> at nested level inside rendered by router LibraryComponent.

Let's add this outlet at the same level as 'sidebar':

<router-outlet name="sidebar"></router-outlet>
<router-outlet name="selection"></router-outlet>

and it actually works stackblitz


Now let's come back to your attempt. If you want to render selection components inside selection.component.html then you should be using nested named routed links:

selection.component.html

[routerLink]="['.', { outlets: { selection: [routeName] } }]"
                \/
           relative path

The above binding will generate nested links like (sidebar:photos/(selection:facebook))

Now you need to move SelectionRoutes configuration to children property of photos path:

selection.module.ts

imports: [
  CommonModule,
  RouterModule, //.forChild(SelectionRoutes)
],

sidebar.routes.ts

import { SelectionRoutes } from '../selection/selection.routes';
...
export const SidebarRoutes: Route[] = [
  { path: 'photos', component: LibraryComponent, outlet: 'sidebar', children: SelectionRoutes },

Stackblitz Example

Update

In order to make facebook as a default subroute you create a route with redirectTo option like:

export const SelectionRoutes: Route[] = [

  { path: 'facebook', component: FacebookComponent, outlet: 'selection' },
  { path: 'google', component: GoogleComponent, outlet: 'selection' },
  { path: '', redirectTo: '/(sidebar:photos/(selection:facebook))', pathMatch: 'full', },
]

Stackblitz Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Thanks a lot for your very well explained and detailed answer ! I'm gonna try the second approach hopping there won't be any issue with the relative path and my main router-outlet (not the sidebar nor selection) because it really feel weird for me to declare the router-outlet on the parent component next to sidebar router-outlet. Doesn't feel right at all :/ – Adrien Martinet Aug 16 '19 at 11:52
  • By the way, default routes are kind of a pain with multiple router outlets. I tried to declare a default route in the selection outlet but it doesn't seems to work : https://stackblitz.com/edit/ng-multiple-router-outlet-with-default-routes?file=src%2Fapp%2Fselection%2Fselection.routes.ts – Adrien Martinet Aug 16 '19 at 12:49
  • Thanks for your quick answer. It works but seems not very clean / modulable to define complete strings like this :(. Not even sure it will work when adding the main routing. – Adrien Martinet Aug 16 '19 at 14:26