angular: Routing: Named outlets do not work properly as child routes

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

I need 2 named outlets for my navigation. For this I followed the official angular docs example and ended up having this:

1) Works fine

RouterModule.forRoot([
            { path: 'secondPage/:id', component: SecondPageComponent, outlet: 'contentSecond' },
            { path: 'firstPage', component: FirstPageComponent, outlet: 'contentFirst' }
], { useHash: true, enableTracing: true })

Which works fine.

In my case I need to put those routes in a parent route with an Authentication Guard:

2.1) Does not work as expected

RouterModule.forRoot([
    {
        canActivate: [],
        path: '',
        children: [
            { path: 'secondPage/:id', component: SecondPageComponent, outlet: 'contentSecond' },
            { path: 'firstPage', component: FirstPageComponent, outlet: 'contentFirst' }
        ]
    }
], { useHash: true, enableTracing: true })

// Using this to route inside the outlet
// this.router.navigate([{ outlets: { contentSecond: ['secondPage', pageNumber] } }]);

Which causes this

Router Event: NavigationStart
platform-browser.es5.js:1017 NavigationStart(id: 2, url: '/(contentSecond:secondPage/1)')
platform-browser.es5.js:1017 NavigationStart {id: 2, url: "/(contentSecond:secondPage/1)"}
platform-browser.es5.js:1026 

Router Event: NavigationError
platform-browser.es5.js:1017 NavigationError(id: 2, url: '/(contentSecond:secondPage/1)', error: Error: Cannot match any routes. URL Segment: 'secondPage/1')
platform-browser.es5.js:1017 NavigationError {id: 2, url: "/(contentSecond:secondPage/1)", error: Error: Cannot match any routes. URL Segment: 'secondPage/1'
    at ApplyRedirects.noMatchError (http…}
core.es5.js:1020 

ERROR Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'secondPage/1'
Error: Cannot match any routes. URL Segment: 'secondPage/1'

2.2) Works but feels weird since 2.1 fails

        RouterModule.forRoot([
            {
                canActivate: [],
                path: '',
                children: [
                    { path: 'example', component: FirstPageComponent }
                ]
            }
        ], { useHash: true, enableTracing: true })

/*
Router Event: NavigationStart
platform-browser.es5.js:1017 NavigationStart(id: 2, url: '/example')
platform-browser.es5.js:1017 NavigationStart {id: 2, url: "/example"}
platform-browser.es5.js:1026 

Router Event: RoutesRecognized
platform-browser.es5.js:1017 RoutesRecognized(id: 2, url: '/example', urlAfterRedirects: '/example', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'example', path:'example') }  } )
platform-browser.es5.js:1017 RoutesRecognized {id: 2, url: "/example", urlAfterRedirects: "/example", state: RouterStateSnapshot}
*/

3) This works partly but fails at the end

RouterModule.forRoot([
    {
        canActivate: [],
        path: '',
        children: [
            {
                path: 'home', children: [{ path: 'firstPage', component: FirstPageComponent, outlet: 'contentFirst' }]
            },
            {
                path: 'secondPage', children: [{ path: ':id', component: SecondPageComponent, outlet: 'contentSecond' }]
            }
        ]
    }
], { useHash: true, enableTracing: true })

/*
1) Works fine
this.router.navigate([
    'secondPage', {
        outlets: { contentSecond: [pageNumber] }
    }
]);

2) Works fine
this.router.navigate([
    'home', {
        outlets: { contentFirst: ['firstPage'] }
    }
]);

3) Fails
this.router.navigate([
    'secondPage', {
        outlets: { contentSecond: [pageNumber] }
    }
]);

Router Event: ResolveStart
platform-browser.es5.js:1017 ResolveStart(id: 4, url: '/secondPage/(contentSecond:1)', urlAfterRedirects: '/secondPage/(contentSecond:1)', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'secondPage', path:'secondPage') { Route(url:'1', path:':id') }  }  } )
platform-browser.es5.js:1017 ResolveStart {id: 4, url: "/secondPage/(contentSecond:1)", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.es5.js:1026 

Router Event: ResolveEnd
platform-browser.es5.js:1017 ResolveEnd(id: 4, url: '/secondPage/(contentSecond:1)', urlAfterRedirects: '/secondPage/(contentSecond:1)', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'secondPage', path:'secondPage') { Route(url:'1', path:':id') }  }  } )
platform-browser.es5.js:1017 ResolveEnd {id: 4, url: "/secondPage/(contentSecond:1)", urlAfterRedirects: UrlTree, state: RouterStateSnapshot}
platform-browser.es5.js:1026 

Router Event: NavigationError
platform-browser.es5.js:1017 NavigationError(id: 4, url: '/secondPage/(contentSecond:1)', error: Error: Cannot activate an already activated outlet)
platform-browser.es5.js:1017 NavigationError {id: 4, url: "/secondPage/(contentSecond:1)", error: Error: Cannot activate an already activated outlet
    at RouterOutlet.activateWith (http://localhos…}
core.es5.js:1020 

ERROR Error: Uncaught (in promise): Error: Cannot activate an already activated outlet
Error: Cannot activate an already activated outlet

Again, this feels odd since routing back and forth is what I did in 1) and it worked without any errors

Expected behavior

2.1) should behave like 1) since 2.2) behaves like 1) and 3) should work, but does not

Minimal reproduction of the problem with instructions

Use code snippets from above and play around with this example http://plnkr.co/edit/4XHwrIirGojj0fAgt5yn?p=preview

What is the motivation / use case for changing the behavior?

Make named outlets work as expected

Environment


Angular version: 4.3.1


Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: v8.0.0  
- Platform:  Windows

Others:

Edit: accidentally posted empty issue… closed it, wrote the issue and re-opened it.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 51
  • Comments: 30 (1 by maintainers)

Most upvoted comments

This is a major issue and should not be in the Backlog…

for 2 years -are you kidding me?

I believe I am running into the same issue. I have a named outlet that is a child of a path-less component. I have a Plunkr for it. The primary outlet seems to work (Layout A / B); but, the sibling named-outlet is breaking with an unexpected error message, Error: Cannot match any routes. URL Segment: 'layout-a'.

Plunkr: https://plnkr.co/edit/3HLVIYDdxycups61L8TA

+1

Same issue with Angular 8.2.8

same problem, child empty-path named route cant override parent’s. For example:

{ path: ‘’, component: HeaderComponent, outlet: ‘header’, }, { path: ‘about’, children: [ { path: ‘’, component: AboutComponent }, { path: ‘’, component: EmptyHeaderComponent, outlet: ‘header’, }, ] },

which causes error: Cannot activate an already activated outlet.

Seams still not being fixed with angular 9. Just posting to ensure that this is not closed like https://github.com/angular/angular/issues/10726

Hi there, still have that problem.

Child named outlet throws error Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: ‘suboutlet’ Error: Cannot match any routes. URL Segment: ‘suboutlet’

Stackblitz example here https://stackblitz.com/edit/angular-6eip3n

The problem is tied to the fact that the RouterModule’s ‘routes’ is configured with an empty path:

const routes: Routes = [{
  path: '', // <-- This is empty. This is the problem.
  component: PageComponent,
  children: [{
    path: 'subpage',
    component: SubpageComponent
  }, {
    path: 'suboutlet',
    component: SuboutletComponent,
    outlet: 'sub'
  }]
}]

Give the root path a name:

const routes: Routes = [{
  path: 'main', // <-- Given a name 
  component: PageComponent,
  children: [{
    path: 'subpage',
    component: SubpageComponent
  }, {
    path: 'suboutlet',
    component: SuboutletComponent,
    outlet: 'sub'
  }]
}]

Then adjust your links in AppComponent’s template by adding ‘main’ to the links:

 <a [routerLink]="['main/subpage']">Go to page</a> // <-- "main" has been added (originally was: ['subpage'])
  <a [routerLink]="['main', {outlets: {sub: ['suboutlet']}}]">Go to page with outlet</a> // <-- "main" has been added (originally was: '')

After making those changes, your StackBlitz example works properly:

image

Same problem. All my routes are child routes. I can navigate them to the default page, but not to the named outlet.

Encountering same issue. Migrating to UI Router as this bug has been for almost a year now, and no workaround available.

Anyone figure out this issue? I am running into the same thing.

I managed to bypass this by placing the auxiliary outlet right inside the child component then creating an empty child route and it works. Here’s my code

const routes: Routes = [
  {
    path:'admin',
    component:AdminIndexComponent,
    children: [
      {
        path:'',
        redirectTo:'home',
        pathMatch:'full',
      },
      {
        path:'home',
        component:HomeComponent,
        children: [
          {
            path:'',
            component:NavbarHomeComponent,
            outlet:'menu'
          }
        ]
      },
      {
        path:'login',
        component:LoginComponent,
        children: [
          {
            path:'',
            component:NavbarOffComponent,
            outlet:'menu'
          }
        ]
      },
    ]
  },
];

for instance my path: 'home', component:HomeComponent is as follows:

<router-outlet name="menu"></router-outlet>
<div>
  <!-- Some content -->
</div>

hope my approach helps 😉

I struggled with this for a good while. I’m still not sure this is the best way, but here’s what I have:

When redirecting to child route that uses an outlet one always has to use /path/to/($outlet:$route) to correctly redirect to that route. I’m not entirely sure what the name for this concept is but here’s something about this in the docs: https://angular.io/guide/router#secondary-route-navigation-merging-routes-during-navigation

Props to the Tab-starter project for Ionic which got me on this trail: https://github.com/ionic-team/starters/blob/master/angular/official/tabs/src/app/tabs/tabs.router.module.ts

Also props to this Angular University tutorial which also effortlessly makes use of nested- and what it calls auxilliary-routes: https://blog.angular-university.io/angular-2-router-nested-routes-and-nested-auxiliary-routes-build-a-menu-navigation-system/


I’m still having some trouble setting up the router in the way I believe it would be correct, e.g. the child routes should not have to have to link to absolute routes all the time. This breaks encapsulation. E.g.:

parent-router.ts:

const routes: Routes = [
  {
    path: '',
    loadChildren: 'child-router.ts#ChildRouterModule',
    canActivate: [AuthGuardService],
    canLoad: [AuthGuardService],
  },
  { 
    path: 'auth/login',
    component: PageLoginComponent,
    canActivate: [AuthGuardReverseService],
  }
];

child-router.ts:

const routes: Routes = [
  {
    path: '',
    redirectTo: '/main/(expert-overview:expert)',
    pathMatch: 'full'
  },
  {
    path: 'main',
    component: TabsMainComponent,
    children: [
      {
        path: '',
        redirectTo: '/main/(expert-overview:expert)',
        pathMatch: 'full',
      },
      {
        path: 'expert',
        outlet: 'expert-overview',
        pathMatch: 'full',
        component: PageExpertOverviewComponent,
      },
    ]
  }
];

If I change redirectTo: '/main/(expert-overview:expert)', of the child-route to redirectTo: '(expert-overview:expert)', I get the error:

ERROR Error: Uncaught (in promise): Error: Only absolute redirects can have named outlets. redirectTo: ‘(expert-overview:expert)’ Error: Only absolute redirects can have named outlets. redirectTo: ‘(expert-overview:expert)’

I believe a child route should not have to know where it is ultimately located since, again, this breaks encapsulation and makes moving around routes unnecessarily hard.


For now I’m just happy that this works. Hope to see some of these things addressed at some point, though.

Some working code:

//this works
    this.router.navigate([{ outlets: { fullscreen: ['add'] } }], {
      relativeTo: this.route
    });

//this works too 
    this.router.navigate([
      '/account/my-properties',
      {
        outlets: { fullscreen: ['add'] }
      }
    ]);

Hi there, still have that problem.

Child named outlet throws error Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: ‘suboutlet’ Error: Cannot match any routes. URL Segment: ‘suboutlet’

Stackblitz example here https://stackblitz.com/edit/angular-6eip3n

For anyone reading this using Angular 7, my answer here might be helpful: https://github.com/angular/angular/issues/10981#issuecomment-479482811

Work around the problem:

const routes: Routes = [
  { path: '', redirectTo: '../main' },
  { path: '../main', component: MainComponent, children: [
    { path: 'left', component: LeftComponent, outlet: 'left' },
    { path: 'right', component: RightComponent, outlet: 'right' },
  ]},
];

Another discover that I made is that the named router outlet should be inside the parent component of the child, cannot be in the parent of the parent (cannot be in the grandparent)