Passing Data Between Tabs in Ionic 2

May 5th 2017 Ionic 2 Event Aggregator

When using tabs in Ionic 2, each one of them contains a page that doesn't know anything about the other pages nor their common parent page hosting the tabs. This makes it challenging to switch tabs or pass data between them.

Passing Values from Parent to Child Page

To pass data from the parent page to a tab, there is rootParams input property on Tab component. Its value will be passed to the child page via the injected NavParams object. To pass an id value to the child page, the parent page needs to bind it to rootParams:

export class TabsPage {
  tab1Root = Page1;
  tab2Root = Page2;
  tab2Params = { id: 1 };
}
<ion-tabs>
  <ion-tab [root]="tab1Root" tabTitle="Page 1"></ion-tab>
  <ion-tab [root]="tab2Root" [rootParams]="tab2Params" tabTitle="Page 2"></ion-tab>
</ion-tabs>

The child page can then read the value in its constructor:

import { NavParams } from 'ionic-angular';

export class Page2 {
  id: number;

  constructor(navParams: NavParams) {
    this.id = navParams.get('id');
  }
}

Switching to a Different Tab from Code

Tabs component provides a convenient select method for changing the currently selected tab, however there is no obvious way to access it from the child page. Inspecting the injected NavController instance in the child page reveals that its parent is actually the Tabs component we are looking for:

import { NavController } from 'ionic-angular';

export class Page1 {
  constructor(private navCtrl: NavController) {}

  changeTab() {
    (this.navCtrl.parent as Tabs).select(1);
  }
}

Although this works, it relies on undocumented Ionic internals, and can easily break in future versions. It also doesn't provide a way to pass along additional data, e.g. a different id value.

Using Events

Fortunately, Ionic 2 comes with a publish-subscribe based message passing system, which can be used instead. The child page can publish a message to a specific topic, notifying the parent page about the tab change request:

import { Events } from 'ionic-angular';

export class Page1 {
  constructor(private events: Events) {}

  changeTab() {
    this.events.publish('change-tab', 1, 2);
  }
}

The parent page can subscribe to this topic and change the tab when it receives the message:

import { ViewChild } from '@angular/core';
import { Tabs, Events } from 'ionic-angular';

export class TabsPage {
  // reference component in page template
  @ViewChild(Tabs) tabs: Tabs;

  tab1Root = Page1;
  tab2Root = Page2;
  tab2Params = { id: 1 };

  constructor(events: Events) {
    events.subscribe('change-tab', (tab, id) => {
      this.tabParams.id = id;
      this.tabs.select(tab);
    });
  }
}

This will only work, if the requested tab hasn't been opened before. Otherwise, the page is already initialized and will not read the new id value. To be notified about it, it must subscribe to the same topic:

import { NavParams, Events } from 'ionic-angular';

export class Page2 {
  id: number;

  constructor(navParams: NavParams, events: Events) {
    this.id = navParams.get('id');

    events.subscribe('change-tab', (tab, id) => {
      this.id = id;
    });
  }
}

If you're planning to use message passing in multiple places in your application, you should come up with a good naming policy for the message topics to make the meaning of each message as clear as possible. You could even declare the names as constants to avoid typos.

Copyright
Creative Commons License