If you ever tried to combine side menu, tabs and login page on Ionic, for sure you know how a mess it could represent. We’ve faced this challenge and would like to share with you how we achieve it and which docs/posts help us on the way. Combine side menu, tabs and login page with Ionic 4

/!\ Update 2019-02-13: Tabs got changes on Ionic 4.0.0-beta.18 (released the 2018-12-13). I’ve applied these updates on src code and post below.

Application

We are going to create a demo App meu-starter.tabs-sidemenu.ionic-v4.

This App will implement following features:

  • side menu with split pane (expand side menu on tablet/desktop)
  • include tabs on main content area
  • login page without side menu neither tabs
  • highlight on side menu current url

We’ll use the latest tab implementation of Ionic released on 4.0.0-beta.18.

Setup

Prerequisites

We need to have [Node.js] and [Git] installed in order to install both [Ionic] and [Cordova].

$ npm install ionic typescript -g
...

$ npm ls -g ionic npm typescript --depth 0
/usr/local/lib
├── @angular/cli@7.0.3
├── ionic@4.2.1 
├── npm@6.4.1 
└── typescript@3.1.1

Create a new Ionic v4 application

Create a New Ionic 4 and Angular 6 Application using tabs template (we could acheive same result using sidemenu template and adding tabs)

$ ionic start meu-starter.tabs-sidemenu.ionic-v4 tabs --type=angular
$ cd meu-starter.tabs-sidemenu.ionic-v4

You can test the App running ionic serve cmd:

$ ionic serve
> ng run app:serve --host=0.0.0.0 --port=8100

To test iOS and Android views I recommend using @ionic/lab package

$ npm i --save-dev @ionic/lab

and run

$ ionic serve --lab

File Structure

Below I show the final file structure of the project. I don’t list all files, only the main files related to this demo App.

./src
  /app
    /about
    /contact
    /home
    /tabs
      tabs.module.ts
      tabs.page.html
      tabs.page.scss
      tabs.page.spec.ts
      tabs.page.ts
      tabs.router.module.ts
    /components
      /menu-item
        menu-item.component.scss
        menu-item.component.html
        menu-item.component.spec.ts
        menu-item.component.ts
    app-routing.module.ts
    app.component.html
    app.component.spec.ts
    app.component.ts
    app.module.ts

/!\ Update 2019-05-28: I’ve recently run this tutorial on Ionic 4.4 and seems tabs starter has changed slightlty: about, contact and home tabs have been replaced by tab1, tab2 and tab3. It doesn’t make a big difference, only naming changes. But on lines below I continue with previous names, pay attention ;-)

Split pane and side menu

We start with a basic implementation of ion-split-pane and ion-menu extracted from official doc.

src/app/app.component.html

<ion-app>
  <ion-split-pane>
    <!--  our side menu  -->
    <ion-menu>
      <ion-header>
        <ion-toolbar>
          <ion-title>Menu</ion-title>
        </ion-toolbar>
      </ion-header>
    </ion-menu>
  
    <!-- the main content -->
    <ion-router-outlet main></ion-router-outlet>
  </ion-split-pane>
</ion-app>

Split pane and side menu

Login page and disable menu

$ ionic g page login
> ng generate page login
CREATE src/app/login/login.module.ts (538 bytes)
CREATE src/app/login/login.page.scss (0 bytes)
CREATE src/app/login/login.page.html (132 bytes)
CREATE src/app/login/login.page.spec.ts (684 bytes)
CREATE src/app/login/login.page.ts (252 bytes)
UPDATE src/app/app-routing.module.ts (385 bytes)

On login page we’d like to disable side menu, then we’ll subscribe to router.events to get current url and compare with /login, if true then call enable(false) method of MenuController.

src/app/app.component.ts

...
export class AppComponent implements OnInit {
  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private menuCtrl: MenuController,
    private router: Router
  ) { }

  ngOnInit() {
    this.router.events.subscribe((event: RouterEvent) => {
      if (event instanceof NavigationEnd && event.url === '/login') {
        this.menuCtrl.enable(false);
      }
    });
  }

On above extract I omit some imports (as RouterEvent, NavigationEnd for example), it’s allow to be short and keep focus on what is important, hope you can find them alone, if needed at the end of this post I share a link to github repo where you can find whole code.

Could also use the disabled property on ion-menu to hide it on login, <ion-menu [disabled]="!isLoggedIn"></ion-menu>

Add menu icon on pages

We add ion-menu-button on our pages to display the ‘hamburguer’ when sidemenu not visible on SplitPane.

src/app/contact/contact.page.html (or src/app/tab3/tab3.page.html on recent release)

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>
      Contact
    </ion-title>
  </ion-toolbar>
</ion-header>
...

We’ll generate dynamic links on sidemenu and create a component menu-item to display the link on ion-item element. We do it because a new component allow us to setup a css where we add active item style.

The active link is marked on app.component.ts as active by comparing its url and current url.

src/app/app.component.ts

export class AppComponent implements OnInit {

  pages = [
    {
      title: 'Login',
      url: '/login',
      icon: 'log-in'
    },
    {
      title: 'Contact',
      url: '/tabs/contact',
      icon: 'person'
    },
    {
      title: 'About',
      url: '/tabs/about',
      icon: 'information-circle'
    }
  ];

  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private router: Router
  ) {
    this.initializeApp();
  }

  ngOnInit() {
    this.router.events.subscribe((event: RouterEvent) => {
      if (event instanceof NavigationEnd) {
        this.pages.map( p => {
          return p['active'] = (event.url === p.url);
        });
      }
    });
  }

src/app/app.component.html

<ion-app>
  <ion-split-pane>
    <!--  our side menu  -->
    <ion-menu>
      <ion-header>
        <ion-toolbar>
          <ion-title>Menu</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list>
          <ion-menu-toggle auto-hide="false" *ngFor="let p of pages">
            <app-menu-item [link]="p"></app-menu-item>
          </ion-menu-toggle>
        </ion-list>
      </ion-content>
    </ion-menu>

    <!-- the main content -->
    <ion-router-outlet main></ion-router-outlet>
  </ion-split-pane>
</ion-app>

We set auto-hide=false because using SpliPane we don’t want to auto hide menu when view is open on desktop.

$ $ ionic g component components/menu-item
> ng generate component components/menu-item
CREATE src/app/components/menu-item/menu-item.component.scss (0 bytes)
CREATE src/app/components/menu-item/menu-item.component.html (28 bytes)
CREATE src/app/components/menu-item/menu-item.component.spec.ts (643 bytes)
CREATE src/app/components/menu-item/menu-item.component.ts (281 bytes)
UPDATE src/app/app.module.ts (943 bytes)
[OK] Generated component!

src/app/components/menu-item/menu-item.component.ts

import { Component, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-menu-item',
  templateUrl: './menu-item.component.html',
  styleUrls: ['./menu-item.component.scss']
})
export class MenuItemComponent implements OnInit {

  @Input() link: any;

  constructor() { }

  ngOnInit() {
  }

}

src/app/components/menu-item/menu-item.component.scss

.active-item {
  border-left: 8px solid var(--ion-color-primary);
}

src/app/components/menu-item/menu-item.component.html

<ion-item [routerLink]="link.url" [class.active-item]="link.active" routerDirection="root">
  <ion-icon [name]="link.icon" slot="start"></ion-icon>
  <ion-label></ion-label>
</ion-item>

Repository & Demo

All source code can be found on GitHub: https://github.com/meumobi/meu-starter.tabs-sidemenu.ionic-v4

Furthermore


Victor Dias

Sharing mobile Experiences

Follow me