'How to implement routed tabs with angular material, within a child route?

I want to use Angular Material tabs https://material.angular.io/components/tabs with a router navigation in the tabs.

I tried to use <nav mat-tab-nav-bar> as indicated in the doc, and I found this tutorial: https://nirajsonawane.github.io/2018/10/27/Angular-Material-Tabs-with-Router/ where I can find a template like that:

<nav mat-tab-nav-bar>
  <a mat-tab-link
    *ngFor="let link of navLinks"
    [routerLink]="link.link"
    routerLinkActive #rla="routerLinkActive"
    [active]="rla.isActive">
    {{link.label}}
  </a>
</nav>
<router-outlet></router-outlet>

But the problem is, that my tabs are not at the root of my application, but are in a submodule in a child route. I have something like that:

In app-routing-module:

const routes: Routes = [
  ...
  { path: 'subpath', loadChildren: () => import('./path-to-module/submodule.module').then(m => m.SubmoduleModule) },
  ...
];

In submodule-routing-module I should have something like that:

const routes: Routes = [
  { path: '', component: FirstTabComponent },
  { path: 'tab2', component: SecondTabComponent },
]

What I would like is that, if I go to url /subpath I see the tabs with the first tab selected, and if I go to url /subpath/tab2 I see the tabs with the second tab selected.

Any idea how to do that?



Solution 1:[1]

I, too, ran into the same issue. I cloned Angular-Material-Tabs-with-Router but modified it with a child component that also has child components.

The child component is called home and in home.component.html it implements:

<nav mat-tab-nav-bar>
  <a mat-tab-link
  *ngFor="let link of navLinks"
  [routerLink]="link.link"
  routerLinkActive #rla="routerLinkActive"
  [active]="rla.isActive">
  {{link.label}}
  </a>
</nav>
<router-outlet></router-outlet>

This creates 3 tabs called Notes, Photos and Documents. The Notes tab includes additional components to list, view, edit and delete notes.

I created and added the source to GitHub and imported it to Stackblitz:

GitHub

Stackblitz

Solution 2:[2]

I had the same issue. The problem came from the routing module.

In order to inherit the imported Material classes, the path for children must be lazy-loaded instead of being directed from the component.

This caused the problem:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';


const routes: Routes = [
  {
    path: 'customers',
    // Don't use this method in order to link to children!
    component: 'CustomerListComponent'

  } 

The 'customer list' template loaded, but without formatting. Also, Angular threw an error because it could not find the 'mat-tab' directive anywhere, even though it was being imported in the parent component, orders.module.ts.

This was the solution: src > app > app-routing.module.ts

const routes: Routes = [{
      path: 'customers',
      loadChildren: () =>
        import ('./customers/customers.module').then(m => m.CustomersModule)
    }

Here are the related files from the project: src > app > orders > orders.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { OrdersRoutingModule } from './orders-routing.module';
import { OrderListComponent } from './order-list/order-list.component';

// Material components
import { MatTabsModule } from '@angular/material/tabs';

@NgModule({
  imports: [
    CommonModule,
    OrdersRoutingModule,
    MatTabsModule
  ],
  declarations: [
    OrderListComponent
  ]
})
export class OrdersModule { }

src > app > orders > orders-list > orders-list.component.html

<mat-tab-group>
  <mat-tab label="Orders">
    <ng-template matTabContent>
      <p>Orders here.</p>
    </ng-template>
  </mat-tab>
  <mat-tab label="Shipments">
    <ng-template matTabContent>
      <p>Shipments here.</p>
    </ng-template>
  </mat-tab>
  <mat-tab label="Archived">
    <ng-template matTabContent>
      <p>Really old orders here.</p>
    </ng-template>
  </mat-tab>
</mat-tab-group>

Solution 3:[3]

If my understanding is right, you want to navigate to a component in a lazy loaded module.

For this I made a stackblitz, which might be useful for you.

Here's the basic code for your understanding

<nav mat-tab-nav-bar class="mat-elevation-z8" >
  <a
    mat-tab-link
    *ngFor="let link of navLinks"
    routerLink="{{ link.location }}"
    routerLinkActive
    #rla="routerLinkActive"
    [active]="rla.isActive"
  >
    <mat-icon>{{ link.icon }}</mat-icon>
    <span>{{ link.label | uppercase }}</span>
  </a>
</nav>

in ts

 navLinks = [
    {location:'',label:'dummy',icon:'menu'},
    { location: '/shared', label: 'Overview', icon: 'account_circle' },
    { location: '/shared/sub', label: 'Experience', icon: 'work' }
  ];

Stackblitz

Solution 4:[4]

An alternative to janders solution is to use the router dependency. With router.url()function, you are able to compare the current active url with your navigation links.

In your Component.ts:

...

rootUrl = '/path/to/your/component';

  navigation = [
    {
      name: 'Table',
      link: '',
    },
    {
      name: 'Stats',
      link: '/stats',
    },
    {
      name: 'Settings',
      link: '/settings',
    },
    {
      name: 'Export',
      link: '/export',
    }
  ];

constructor(private route: ActivatedRoute, public router: Router) {
...
}

...

In your Component.html:

...

  <nav mat-tab-nav-bar mat-align-tabs="left">
    <a mat-tab-link
       *ngFor="let l of navigation"
       [routerLink]="rootUrl + l.link"
       [active]="this.router.url === rootUrl + l.link">
      {{l.name}}
    </a>
  </nav>

...

In your router:


...

  {
    path: '/path/to/your/component',
    component: Component,
    canActivate: [AuthGuard],
    children: [
      {
        path:'',
        pathMatch: 'full',
        component: TableComponent
      },
      {
        path:'settings',
        pathMatch: 'full',
        component: SettingsComponent
      },
      {
        path:'export',
        pathMatch: 'full',
        component: ExportComponent
      },
      {
        path:'stats',
        pathMatch: 'full',
        component: StatsComponent
      }
    ]
  }

...

Solution 5:[5]

the same code previously see, but updated

************* code updated ********  (12-05-2022, version angular recent)
              <nav mat-tab-nav-bar >
                      <a mat-tab-link *ngFor="let link of navLinks"
                               (click)="activeLink = link.link"
                               [routerLink]="link.link"
                               [active]="activeLink == link.link"> {{link.label}} </a>
                    </nav>
              <router-outlet></router-outlet>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Treycos
Solution 3 Dharman
Solution 4 Seb
Solution 5 lura_river