Ionic 4 / Angular 6 : Nested child routes in tabs

29,378

Solution 1

With 4.0.0-beta.18 ion-tab was removed and it's not necessary to use named outlets.

Demo (with two different approaches) + Explanation:
https://github.com/servrox/demo-ionic-tab-routing

Ionic CLI version 4.10.3
Ionic Framework @ionic/angular 4.0.1

Solution 2

This all seemed extremely confusing to me. Then i realised that all i had to do in Tabs Routing was.

const routes: Routes = [
  { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
  { path: '',
    component: TabsPage,
    children: [
      { path: 'tab1', loadChildren: '../tab1/tab1.module#tab1Module'},
      { path: 'tab2', 
        children: [
          { path: '', loadChildren: '../tab2/tab2.module#tab2Module'},
          { path: ':ID', loadChildren: '../tab2/tab2details.module#tab2detailsModule'},
        ]
      },
    ]
  },
];

Where Tab2 has a List Page and a details page.

List Page URL : /tabs/tab2

Details Page URL : /tabs/tab2/123/

The Tab2 Tab stays active when your on the list or details page, and the back button shows up when your on the details page.

Solution 3

This is how I did. In tabs.router.module.ts,

const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'featured',
        children: [
          {
            path: '',
            loadChildren: '../tab-featured/tab-featured.module#TabFeaturedPageModule'
          }
        ]
      },
      {
        path: 'featured/:id',
        children: [
          {
            path: '',
            loadChildren: '../tab-featured-detail/tab-featured-detail.module#TabFeaturedDetailPageModule'
          }
        ]
      },
      {
        path: 'categories',
        children: [
          {
            path: '',
            loadChildren: '../tab-category/tab-category.module#TabCategoryPageModule'
          }
        ]
      },
      {
        path: 'popular',
        children: [
          {
            path: '',
            loadChildren: '../tab-popular/tab-popular.module#TabPopularPageModule'
          }
        ]
      },
      {
        path: '',
        redirectTo: '/tabs/featured',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/featured',
    pathMatch: 'full'
  }
];

tab-featured-detail.module.ts

import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TabFeaturedDetailPage } from './tab-featured-detail.page';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    RouterModule.forChild([{ path: '', component: TabFeaturedDetailPage }])
  ],
  declarations: [TabFeaturedDetailPage]
})
export class TabFeaturedDetailPageModule {}

Solution 4

So this is the cleanest way I found so far to do it.

Here the tabs.router.module.ts

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

import { TabsPage } from './tabs.page';
import { DeliveriesPage } from '../deliveries/deliveries.page';
import { AboutPage } from '../about/about.page';
import { ContactPage } from '../contact/contact.page';
import { DetailsPage } from '../details/details.page';

const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: '',
        redirectTo: '/tabs/(deliveries:deliveries)',
        pathMatch: 'full',
      },
      {
        path: 'deliveries',
        outlet: 'deliveries',
        component: DeliveriesPage,
      },
      {
        path: 'details/:id',
        outlet: 'deliveries',
        component: DetailsPage
      },
      {
        path: 'about',
        outlet: 'about',
        component: AboutPage
      },
      {
        path: 'contact',
        outlet: 'contact',
        component: ContactPage
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/(deliveries:deliveries)',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}

Import the DetailsModulePage in tabs.module.ts

import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { TabsPageRoutingModule } from './tabs.router.module';

import { TabsPage } from './tabs.page';
import { ContactPageModule } from '../contact/contact.module';
import { AboutPageModule } from '../about/about.module';
import { DeliveriesPageModule } from '../deliveries/deliveries.module';
import { DetailsPageModule } from '../details/details.module';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    TabsPageRoutingModule,
    DeliveriesPageModule,
    AboutPageModule,
    ContactPageModule,
    DetailsPageModule
  ],
  declarations: [TabsPage]
})
export class TabsPageModule {}

Here the DetailsModulePage (pretty basic)

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';

import { IonicModule } from '@ionic/angular';

import { DetailsPage } from './details.page';

const routes: Routes = [
  {
    path: '',
    component: DetailsPage,
  }
];

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule.forChild(routes)
  ],
  declarations: [DetailsPage]
})
export class DetailsPageModule {}

And this is the way to access DetailsPage

  • /tabs/(deliveries:details/2)

If you have a better way to do it, i'll be happy know it.

Share:
29,378
Brieuc
Author by

Brieuc

Full-stack developer (PHP, Symfony, WP, VueJS, React, Angular, Typescript). CTO 30+ employees startup. A true believer of distributed & remote teams. Self-taught in programming. Studied computer science & Information system. Strong: PHP, Javascript, MySQL, problems solving. Also: Firebase/Firestore, Ionic/Capacitor, API Platform.

Updated on July 09, 2022

Comments

  • Brieuc
    Brieuc almost 2 years

    I generated an app with the Ionic Tabs Component.

    Tabs are Deliveries, About and Contact.

    Then I generated a page Details.

    I would like the page Details to be a child of Deliveries tab.

    Meaning when I'm on a details page, I'm navigating through the deliveries tab.

    Someone asked a similar question on Ionic Forum

    The directory structure generated :

    - about
    - contact
    - deliveries
        deliveries.module.ts
    - details
        details.module.ts
    - tabs
        tabs.module.ts
        tabs.page.html
        tabs.router.module.ts
    app.module.ts
    app-routing.module.ts
    

    This is the generated app-routing.module.ts

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    const routes: Routes = [
      { path: '', loadChildren: './tabs/tabs.module#TabsPageModule' },
    ];
    @NgModule({
      imports: [
        RouterModule.forRoot(routes)
      ],
      exports: [RouterModule]
    })
    export class AppRoutingModule {}
    

    This is the generated tabs.router.module.ts

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    
    import { TabsPage } from './tabs.page';
    import { DeliveriesPage } from '../deliveries/deliveries.page';
    import { AboutPage } from '../about/about.page';
    import { ContactPage } from '../contact/contact.page';
    
    const routes: Routes = [
      {
        path: 'tabs',
        component: TabsPage,
        children: [
          {
            path: '',
            redirectTo: '/tabs/(deliveries:deliveries)',
            pathMatch: 'full',
          },
          {
            path: 'deliveries',
            outlet: 'deliveries',
            component: DeliveriesPage
          },
          {
            path: 'about',
            outlet: 'about',
            component: AboutPage
          },
          {
            path: 'contact',
            outlet: 'contact',
            component: ContactPage
          }
        ]
      },
      {
        path: '',
        redirectTo: '/tabs/(deliveries:deliveries)',
        pathMatch: 'full'
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule]
    })
    export class TabsPageRoutingModule {}
    

    This is a generated tabs.page.html

    <ion-tabs>
      <ion-tab label="Deliveries" icon="bicycle" href="/tabs/(deliveries:deliveries)">
        <ion-router-outlet name="deliveries"></ion-router-outlet>
      </ion-tab>
      <ion-tab label="About" icon="information-circle" href="/tabs/(about:about)">
        <ion-router-outlet name="about"></ion-router-outlet>
      </ion-tab>
      <ion-tab label="Contact" icon="contacts" href="/tabs/(contact:contact)">
        <ion-router-outlet name="contact"></ion-router-outlet>
      </ion-tab>
    </ion-tabs>
    

    This is a generated deliveries.module.ts

    import { IonicModule } from '@ionic/angular';
    import { RouterModule } from '@angular/router';
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    
    import { DeliveriesPage } from './deliveries.page';
    
    @NgModule({
      imports: [
        IonicModule,
        CommonModule,
        FormsModule,
        RouterModule.forChild([
          {
            path: '',
            component: DeliveriesPage
          }
        ])
      ],
      declarations: [
        DeliveriesPage
      ]
    })
    export class DeliveriesPageModule {}
    

    This is details.module.ts

    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { FormsModule } from '@angular/forms';
    import { Routes, RouterModule } from '@angular/router';
    
    import { IonicModule } from '@ionic/angular';
    
    import { DetailsPage } from './details.page';
    
    const routes: Routes = [
      {
        path: '',
        component: DetailsPage,
      }
    ];
    
    @NgModule({
      imports: [
        CommonModule,
        FormsModule,
        IonicModule,
        RouterModule.forChild(routes)
      ],
      declarations: [DetailsPage]
    })
    export class DetailsPageModule {}
    

    So, all my attempts failed, I tried to follow the same logic as above.

    I tried to work in deliveries.module.ts

    RouterModule.forChild([
      {
        path: '',
        component: DeliveriesPage,
      },
      {
        path: 'details',
        loadChildren: '../details/details.module#DetailsPageModule'
      }
    ]),
    

    I also tried

    RouterModule.forChild([
      {
        path: '',
        component: DeliveriesPage,
      },
      {
        path: 'details',
        outlet: 'deliveries
        loadChildren: '../details/details.module#DetailsPageModule'
      }
    ]),
    

    Or

    RouterModule.forChild([
      {
        path: '',
        component: DeliveriesPage,
        children: [
          {
            path: 'details',
            loadChildren: '../details/details.module#DetailsPageModule'
          }
        ]
      },
    ]),
    

    Can't find a way to access details page from

    • /tabs/(deliveries:deliveries)/details
    • /tabs/(deliveries:details)

    Is it possible to achieve that? It is a little bit confusing.

    The easy way right now to access details page is to define the route in app-routing.module.ts, but it would not be part of the same router outlet.

  • Brieuc
    Brieuc over 5 years
    Hi, could we see what is in tab-featured-detail.module or any other module? Does it contain more nested routes? Cheers.
  • Sem
    Sem over 5 years
    tab-featured-detail.module does not contain more nested routes. In my example above, I am making a master-detail pattern for featured, but I had problem setting up the routes until I found your solution. And eventually I just make featured-detail to be same level as featured. This might not be the perfect solution, but it does work for me. I will add in my tab-featured-detail-module.