How to Combine Ionic Side Menu and Tabs Navigation Last update: 2017-07-04

How to Combine Ionic Side Menu and Tabs Navigation

The navigation inside Ionic projects is in general quite simple, but once you try to combine different navigation patterns things can get really tricky. Especially one case happens a lot in reality, which is using an Ionic Side Menu and a Tab Bar navigation together.

Inside this tutorial we will see how we can put together our different view elements in a way that makes sense and works super easily. Some of the code is inspired by the official Ionic conference app, so if you want an even bigger example you can check that repo out.

We will keep things a bit simpler, so in the end we will be able to transition from a dummy login page to our “inside” navigation which combines side menu and tabs.

ionic-side-menu-tabs

Starting our App

We start with a blank template just so we can see what pages and components are needed to put together this kind of navigation. Of course we could also start with the given Ionic templates like side menu or tabs, but I felt starting at zero helps us learn the concepts even more.

We need to create quite a few pages which we will fill with live later on, so go ahead and create everything we need right in the beginning:

ionic start fullNavigation blank
cd fullNavigation
ionic g page login
ionic g page menu
ionic g page tabs
ionic g page tab1
ionic g page tab2
ionic g page special

You can delete the default home/ folder as he doesn’t contain a modules file and we want to use lazy loading. You could also create a file for that inside the folder, but we have generated already everything new so we’re already behind that point now ;)

Next we need to change the entry point of our app to be our new created LoginPage, so open your src/app/app.component.ts and change it to:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage:any = 'LoginPage';

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() => {
      statusBar.styleDefault();
      splashScreen.hide();
    });
  }
}

Now the app will start with the blank login page. Also if you remove the HomePage folder, make sure to remove the reference to it from the app.module.ts!

The Login Screen

Our login screen is actually just a placeholder for your login, we won’t implement anything like this now. You can check out the login example for more ideas how to build your login!

We only need a button so we can move on from that screen, so open your src/pages/login/login.html and change it to:

<ion-header>
  <ion-navbar>
    <ion-title>Login</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <button ion-button full (click)="doLogin()">Login</button>
</ion-content>

The function inside the class will now change our navigation by calling setRoot() which basically resets the navigation instead of pushing a new page onto the stack. Otherwise we would have a back arrow to the login page which we don’t really want.

Go ahead and add this to your src/pages/login/login.ts:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-login',
  templateUrl: 'login.html',
})
export class LoginPage {

  constructor(public navCtrl: NavController, public navParams: NavParams) { }

  doLogin() {
    this.navCtrl.setRoot('MenuPage');
  }

}

So now after we hit login, we will be on the template for our menu. But there’s no menu yet, so let’s change that.

Building the Menu Navigation

The menu pattern is always the same, we need to define the menu area and also the content area inside a view. In our case we construct the menu items from an array of pages which we will create in the next step.

Each button will automatically close the menu using menuToggle and we also dynamically set the icon and color of the button. Setting the color helps to reflect the changes of the tab bar also inside the menu!

Go ahead and add this menu code to your src/pages/menu/menu.html:

<ion-menu [content]="content">
  <ion-header>
    <ion-toolbar>
      <ion-title>Menu</ion-title>
    </ion-toolbar>
  </ion-header>

  <ion-content>
    <ion-list>
      <button ion-item menuClose *ngFor="let p of pages" (click)="openPage(p)">
          <ion-icon item-start [name]="p.icon" [color]="isActive(p)"></ion-icon>
          {{ p.title }}
        </button>
    </ion-list>
  </ion-content>
</ion-menu>

<!-- main navigation -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>

Now we get to the actually most difficult part of this tutorial, because once we select a menu item we need to change our content view and also make sure our tab bar also marks the correct item.

As said before we will keep an array of pages, and those are of the type PageInterface which we defined above the class.

You will see why each element needs the attributes of that interface once we go through the functions.

Also, we grab a reference to the root navigation by using @ViewChild so we can directly work on that element!

Inside the array we keep 3 pages; two of them will be also tabs and 1 is just a special page which should have no tabs visible.

The important part here are now the 2 functions that are called once we open a page from the side menu and the one to check if the current element should be marked as active. For now add the code to your src/pages/menu/menu.ts:

import { Tab2Page } from './../tab2/tab2';
import { Tab1Page } from './../tab1/tab1';
import { TabsPage } from './../tabs/tabs';
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavController, Nav } from 'ionic-angular';

export interface PageInterface {
  title: string;
  pageName: string;
  tabComponent?: any;
  index?: number;
  icon: string;
}

@IonicPage()
@Component({
  selector: 'page-menu',
  templateUrl: 'menu.html',
})
export class MenuPage {
  // Basic root for our content view
  rootPage = 'TabsPage';

  // Reference to the app's root nav
  @ViewChild(Nav) nav: Nav;

  pages: PageInterface[] = [
    { title: 'Tab 1', pageName: 'TabsPage', tabComponent: 'Tab1Page', index: 0, icon: 'home' },
    { title: 'Tab 2', pageName: 'TabsPage', tabComponent: 'Tab2Page', index: 1, icon: 'contacts' },
    { title: 'Special', pageName: 'SpecialPage', icon: 'shuffle' },
  ];

  constructor(public navCtrl: NavController) { }

  openPage(page: PageInterface) {
    let params = {};

    // The index is equal to the order of our tabs inside tabs.ts
    if (page.index) {
      params = { tabIndex: page.index };
    }

    // The active child nav is our Tabs Navigation
    if (this.nav.getActiveChildNav() && page.index != undefined) {
      this.nav.getActiveChildNav().select(page.index);
    } else {
      // Tabs are not active, so reset the root page
      // In this case: moving to or from SpecialPage
      this.nav.setRoot(page.pageName, params);
    }
  }

  isActive(page: PageInterface) {
    // Again the Tabs Navigation
    let childNav = this.nav.getActiveChildNav();

    if (childNav) {
      if (childNav.getSelected() && childNav.getSelected().root === page.tabComponent) {
        return 'primary';
      }
      return;
    }

    // Fallback needed when there is no active childnav (tabs not active)
    if (this.nav.getActive() && this.nav.getActive().name === page.pageName) {
      return 'primary';
    }
    return;
  }

}

First of all when we want to transition to a page, we grab the optional index for that page. Also, we try to get the active child nav, which would mean a reference to the tabs navigation.

If we have both of these information, we can simply use the select() function of the tab bar to select the tab with the according index. Of course the index in our array should reflect the position of the element in the tab bar (we will create the tab bar in the next step).

When the check fails, we more or less reset our root navigation of the content to the new page and pass any additional params to it. This will be the case when we select our special page which has no tab bar, or when we navigate back from that page because then also no tab bar is active (at the moment of the transition).

_Any questions so far? _Just let me know below, I know this is not super easy.

To mark the active element we again try to grab a reference of the tab bar and check if the selected root is equal to the component specified for the page.

As a fallback for our special page we also make a check on the root navigation and see if the names match, by doing this we can also mark our special page as active correctly!

That was the hard part of this tutorial. If you are still with me, things will get easier from here. Take a moment to go through these 2 functions, initially it also took me a moment (or 2, 3…) to completely understand what’s going on here.

Adding the Tab bar

As we approach the end, we haven’t actually added our tabs, so change this by opening your src/pages/tabs/tabs.html and change it to:

<ion-tabs [selectedIndex]="myIndex">
  <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="contacts"></ion-tab>
</ion-tabs>

A very classic tab bar implementation with 2 tabs. The only really important thing here is that we are able to dynamically change the selectedIndex of our tab bar!

We will make use of this inside the constructor of this class. If you remember, we create new pages and also pass the params object through, so in case our tab bar is created newly we can access the tabIndex which was passed through from the menu.ts!

By doing this our tabs will be always initialised back with the correct tab to be selected. Go ahead and change your src/pages/tabs/tabs.ts to:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-tabs',
  templateUrl: 'tabs.html',
})
export class TabsPage {

  tab1Root: any = 'Tab1Page';
  tab2Root: any = 'Tab2Page';
  myIndex: number;

  constructor(navParams: NavParams) {
    // Set the active tab based on the passed index from menu.ts
    this.myIndex = navParams.data.tabIndex || 0;
  }
}

Now we only need to add a menu button to all of our pages so we can toggle the menu from everywhere. Go through these 3 files and change all of them like in the example below, perhaps change the content and title so you can actually see that a different page is displayed!

Files to change:

  • src/pages/tab1/tab1.html
  • src/pages/tab2/tab2.html
  • src/pages/special/special.html

The general HTML template for these files:

<ion-header>
  <ion-navbar>
    <ion-buttons start>
      <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    </ion-buttons>
    <ion-title>Special</ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  This page has no tabs!
</ion-content>

Now all you need to do is run the app and enjoy your full side menu with tabs navigation!

Conclusion

It’s a bit tricky to get all the features working when combining different navigation patterns. But the important thing here is it is definitely possible, and the result is exactly what the behaviour should look like!

Leave a comment below if you enjoyed this a bit different tutorial from what I normally do on some of the more “core” aspects of Ionic!

You can also find a video version of this article below with even more explanation!

Happy Coding, Simon