39

Is there a way to display a child route in the parent route's <router-outlet>?

For example, let's say we have two routes:

/users/12345

/users/12345/orders/12345

I need the second to be a child of the first, but I also need to completely replace the contents of /users/12345 with the contents of /users/12345/orders/12345 opposed to having /users/12345/orders/12345 in the /users/12345 sub router-outlet.

I thought I could do this by just naming the parent level router-outlet and have both routes target it, but from the research I've been doing (and the errors it caused) I get the feeling the names were intended for secondary uses when there already exists a primary router-outlet

John
  • 17,163
  • 16
  • 65
  • 83

6 Answers6

30

I had the same issue. This is how I fixed it:

const routes: Routes = [
   {
    path: '',
    children: [
         {
           path: '',
           component: ParentComponent,
         },
         {
           path: 'child',
           component: ChildComponent,
         }
     ]
   }
];  
krummens
  • 837
  • 2
  • 17
  • 38
N. berouain
  • 1,181
  • 13
  • 17
  • 3
    what if you have a child routing module too? – Ali Sajid Apr 02 '18 at 10:35
  • 4
    I've got to same structure but there is a problem. Parent component route is actually a sibling to child route. This makes problems for example if you need to resolve some data for parent component but also pass it to child. – George Knap Apr 15 '19 at 20:35
26

You can do it by setting your routes like this :

const routes : Routes = [
  { 
    path : 'user/:id',
    component : ParentComponent,
    children : [
      { path : '', component : UserComponent },
      { path : 'order/:id', component : OrderComponent }
    ]
  }
]

ParentComponent's template will have only the <router-outlet> to load its children.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
YounesM
  • 2,279
  • 15
  • 28
15

If you need to hide something (like a datagrid, or instruction panel) based upon a child route being active you can simply use this:

<div *ngIf="outlet.isActivated == false">
   Please select a child route!
</div>

<router-outlet #outlet="outlet"></router-outlet>

It is important to include #outlet="outlet" with the quotes because you're exporting a template variable reference.

There are also events on router-outlet for activation and deactivation.

An alternative is to get the child route when the NavigationEnd event occurs, and then make decisions what to show or hide. For simpler cases the first approach should work fine.

Also not relevant to your question I don't think, but you can completely hide a router-outlet with an *ngIf as you would anything else.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • This worked for me but I have multiple sections and i want to hide/show them. Hide/show basically switches back and forth between parent and child component. i have parent (section 1, 2, and 3). however, when section 1 loads child component , then section 1 of parent component hides as you said. Now i need to do same for section 2 and 3. Now i see that section 1 is getting affected by 2 , as outlet is common. is there a way around this ? Thanks – kuldeep Apr 01 '19 at 09:01
  • @kuldeep It might not be acceptable for you or anyone facing the same problem, but in my case, I had a wrapper component for each section, so each had its own outlet. – Pif Sep 21 '22 at 08:44
5

edit: I've come up with a new solution revolving around using template directives that allows for setting up routes hierarchically opposed to at the same level.

The sample code/demo can be found here: https://stackblitz.com/edit/angular-routing-page-layout

Updated version (2019): https://stackblitz.com/edit/angular-routing-page-layout-cnjpz8

John
  • 17,163
  • 16
  • 65
  • 83
  • What about my answer ? Is it not what you're looking for ? – YounesM Mar 17 '17 at 14:55
  • Using your example, the question was regarding how to replace 'ParentComponent' with 'OrderComponent' when on 'order/:id' in router-outlet, which that setup would not accomplish – John Mar 17 '17 at 15:44
  • Hi, @John. This is a brilliant solution! But how do you navigate to another route same level as the primary? It would cause an error of "Cannot read property 'createEmbeddedView' of undefined" – jedion Jun 21 '18 at 09:30
  • If the situation is both routes set 'component: PageLevelComponent' and angular is re-using the existing component from the first route, I'm not sure. I'm sure we'll run into the same issue once we get more pages situated this way – John Jun 21 '18 at 14:22
  • Thank you, John. This is working so nicely for me, so far. Have you made any improvements to this? I hope to use it extensively, so would appreciate any updates. One thing I had to do, was wrap the `renderContainer` view lines in an `if (stack.length)` check. But thanks, I can't believe this is not a bigger issue for Angular routing! – Sean Aug 21 '19 at 19:43
  • @Sean - It looks like my current implementation is quite a bit different: I'll try and post an updated version when I have a moment – John Aug 22 '19 at 15:39
  • @John - just a nudge. If you're able to provide an update, that would be suprt helpful. – Sean Aug 30 '19 at 07:41
  • 1
    @Sean - I updated the answer with a link containing the version I currently use – John Sep 03 '19 at 17:48
3
 let routes = [
  { 
    path: 'users/:id',
    component: UsersComponent
  },
  {
    path: 'users/:id/orders/:id',
    component: OrdersComponent
  }
 ];
Julia Passynkova
  • 17,256
  • 6
  • 33
  • 32
  • This solution has some tricky problems, because if there's a component-specific service, say `UserService` provided in `UsersComponent`, it won't be available to `OrdersComponent` because it isn't a child of `UsersComponent`. – marked-down Jul 31 '20 at 07:38
0

I had the same issue. This is how I fixed it:

const routes: Routes = [
   {
       path: 'parent',
       component: ParentComponent,
   },
   {
       path: 'parent',
       children: [
            {
                path: 'child',
                component: ChildComponent,
            }
       ],
   },
];

This way the router-outlet will use the correct component and preserve the idea of parent-child relation.

The downside of this approach is that if you need to pass data from the parent to the child you would probably need to fetch it from a service or something similar or duplicate some sort of resolver in both parent paths.