geochr
geochr

Reputation: 271

Angular 11 cannot route to named outlet

We have a basic application with 2 tabs, each with its own named outlet

<mat-tab-group>
    <mat-tab label="Tab1">
        <router-outlet name="tab1"></router-outlet>
    </mat-tab>
    <mat-tab label="Tab2">
        <router-outlet name="tab2"></router-outlet>
    </mat-tab>
</mat-tab-group>

One of the 2 named outlets just renders a component and that's fine. For the second one, we want to be able to route between different views so we created its own module and lazy load it.

App routing looks like

const routes: Routes = [{
    path: '',
    children: [{
        path: '',
        redirectTo: '/home',
        pathMatch: 'full'

    }, {
        path: 'login',
        loadChildren: () => import('./login/login.module').then(m => m.LoginModule)
    },
    {
        path: 'home',
        loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
    }]
}]

Home page's routing looks like

const routes: Routes = [{
    path: '',
    component: HomeComponent,
    children: [{
        path: '',
        component: Tab1Component,
        outlet: 'tab1'
    }, {
        path: '',
        outlet: 'tab2',
        loadChildren: () => import('./tab2/tab2.module').then(m => m.Tab2Module)
    }]
}];

Tab2Module is implemented as follows

const routes: Routes = [{
    path: '',
    component: Tab2Child1Component
}, {
    path: 'tab2-child',
    outlet: 'tab2',
    component: Tab2Child2Component
}]

Tab2Child1Component gets rendered fine but we cannot navigate to Tab2Child2Component.

I am trying the following:

this.router.navigate([{ outlets: { tab2: 'tab2-child' } }], { relativeTo: this.route });

I can see in tracing that router is indeed trying to route to /home/(tab2:tab2-child) but in the end it just redirects back to /home with an error:

 Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'home'
Error: Cannot match any routes. URL Segment: 'home'

Stackblitz showcasing the error: https://stackblitz.com/edit/angular-ivy-pv5jsi

Upvotes: 1

Views: 1887

Answers (1)

Aleš Doganoc
Aleš Doganoc

Reputation: 12082

You have two issues with your design that cause it not to work.

  1. Your home component has empty path which does not work with named outlets. They work only if the path of the of the component is named. Maybe this will change in the future since there is an open bug regarding this in the Angular GitHub.
  2. The components you display in the named outlets must be direct children of the component that contains the outlets in the routing configuration. Your configuration with loadChildren for tab2 is like the component inside tab2 will have more internal routes which is not true in your case since you do not have another router outlet in the Tab2Child1Component.

To change your sample to something that works you need to add a path to the HomeComponent inside the HomeModule. To lazy load the tabs you need to put all the tabs in one module that is loaded as the children of the home component.

Here is my implementation of the HomeModule with the changes needed.

const routes: Routes = [
  {
    path: '',
    redirectTo: 'hc',
    pathMatch: 'full'
  },
  {
    path: 'hc',
    component: HomeComponent,
    loadChildren: () => import("./tab.module").then(m => m.TabModule)
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  declarations: [HomeComponent]
})
export class HomeModule {}

And the implementation of the tab module that defines the tabs for both outlets.

const routes: Routes = [
  {
    path: '',
    component: Tab1Component,
    outlet: "tab1"
  },
  {
    path: '',
    outlet: 'tab2',
    component: Tab2Child1Component
  },
  {
    path: 'tab2-child',
    outlet: 'tab2',
    component: Tab2Child2Component
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  declarations: [Tab2Child1Component, Tab2Child2Component]
})
export class TabModule {}

In the app module redirect directly to the sub path for some reason the redirect in the module does not work if you redirect just to home.

 {
   path: "",
   redirectTo: "/home/hc",
   pathMatch: "full"
 },

Here is a link to a fork of your StackBlitz with the full implementation.

If it is a requirement for you to have separate lazy load modules for each tab then you need to create separate components for each tab that have one router outlet inside to display the tab child components so you get a two level navigation structure. Since I don't know what is your exact use case I cannot say if this would be a better solution.

Upvotes: 2

Related Questions