20

I want to execute some code every time the page changes.

I could add add an ngOnDestroy method to every page. It appears that I could use Ionic 2 page lifecycle hooks (e.g. ionViewDidUnload) for the exact same effect, but I haven't bothered to test that.I would rather add a single method to the main app class.

I see that you can subscribe to Angular 2 Router events. How do I translate that for use in Ionic 2? I'm getting an error with import { Router } from '@angular/router; in the first place:

TypeScript error: <path>/node_modules/@angular/router/src/common_router_providers.d.ts(9,55): Error TS2305: Module '"<path>/node_modules/@angular/core/index"' has no exported member 'NgModuleFactoryLoader'.
TypeScript error: <path>/node_modules/@angular/router/src/router.d.ts(14,39): Error TS2305: Module '"<path>/node_modules/@angular/core/index"' has no exported member 'NgModuleFactoryLoader'.
TypeScript error: <path>/node_modules/@angular/router/src/router_module.d.ts(9,10): Error TS2305: Module '"<path>/node_modules/@angular/core/index"' has no exported member 'ModuleWithProviders'.

webpack config

var path = require('path');


module.exports = {
  entry: [
    path.normalize('es6-shim/es6-shim.min'),
    'reflect-metadata',
    path.normalize('zone.js/dist/zone'),
    path.resolve('app/app.ts')
  ],
  output: {
    path: path.resolve('www/build/js'),
    filename: 'app.bundle.js',
    pathinfo: false // show module paths in the bundle, handy for debugging
  },
  module: {
    loaders: [
      {
        test: /\.ts$/,
        loader: 'awesome-typescript',
        query: {
          doTypeCheck: true,
          resolveGlobs: false,
          externals: ['typings/browser.d.ts']
        },
        include: path.resolve('app'),
        exclude: /node_modules/
      }
    ],
    noParse: [
      /es6-shim/,
      /reflect-metadata/,
      /zone\.js(\/|\\)dist(\/|\\)zone/
    ]
  },
  resolve: {
    alias: {
      'angular2': path.resolve('node_modules/angular2')
    },
    extensions: ["", ".js", ".ts"]
  }
};

It would make more sense to me anyways if there is a way to use the Nav or NavController service from ionic-angular. Is there a way to do that?

sebaferreras
  • 44,206
  • 11
  • 116
  • 134
jmilloy
  • 7,875
  • 11
  • 53
  • 86

6 Answers6

11

Ionic2 doesn't use Angular2's Router. They have their own implementation NavController.


I see that you can subscribe to Angular 2 Router events. How do I translate that for use in Ionic 2?

You can merge all the NavController Events and subscribe to it.

allEvents = Observable.merge(
                 this.navController.viewDidLoad, 
                 this.navController.viewWillEnter, 
                 this.navController.viewDidEnter, 
                 this.navController.viewWillLeave, 
                 this.navController.viewDidLeave, 
                 this.navController.viewWillUnload);

allEvents.subscribe((e) => {
    console.log(e);
});
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89
  • Where would I put this code? I tried adding it to the app.ts constructor without success. – jmilloy Oct 10 '16 at 18:49
  • 1
    you can only use it after declaring atleast one `NavController` instance, it's a little different than Angulr's router – Ankit Singh Oct 11 '16 at 04:21
  • 1
    I added it to my home/welcome page constructor, and it seems to work. Thanks. I'd like to put the code somewhere less obscure (in `app.ts`). Is there any way to do that? When I tried to inject the NavController in the app constructor, it says `no provider for NavController.' – jmilloy Oct 13 '16 at 15:16
  • Thanks, `viewWillUnload` worked for me so far. I'm still interested in locating it in `app/app.ts` instead of obscurely in `pages/home/home.ts`. – jmilloy Oct 13 '16 at 18:12
11

Another option would be to create a super class where you can use the ionViewDidUnload method (or any other lifecycle hook) like this:

import { Events } from 'ionic-angular';

export class BasePage {

  constructor(public eventsCtrl: Events) { }

  ionViewDidEnter() {
    this.eventsCtrl.publish('page:load');   
  }

  ionViewDidUnload() {
    this.eventsCtrl.publish('page:unload');   
  }
}

Then in every page you just need to extend that BasePage

@Component({
  templateUrl: 'build/pages/my-page/my-page.html',
})
export class MyPage extends BasePage {

constructor(private platform: Platform,
              private nav: NavController, 
              private menuCtrl: MenuController,
              ...,
              eventsCtrl: Events) //notice that this one does not have the private/public keyword 
  {    

    // Due to an issue in angular, by now you must send the dependency to the super class
    // https://github.com/angular/angular/issues/5155
    super(eventsCtrl);

    //...
}

And then, you can add a method like this one in the main app.ts file to respond to those events:

  private initializeEventHandlers() {

    this.events.subscribe('page:load', () => {
      // your code...
    });

    this.events.subscribe('page:unload', () => {
      // your code...
    });

  }
sebaferreras
  • 44,206
  • 11
  • 116
  • 134
  • 1
    I do rather like this option. On the other hand, if I'm going to be smashing my face against the horror that is ionic and angular, why do I have to resort to vanilla typescript to do something like this? – jmilloy Oct 13 '16 at 18:15
10

As of Ionic 3.6, you can use the App component to subscribe to application-wide page change events, as documented in : https://ionicframework.com/docs/api/components/app/App/

For example, if you'd like to track every view change in Google Analytics using the GA cordova plugin, you could amend your app.component.ts like this :

constructor(private app: App, private platform: Platform, private ga: GoogleAnalytics, ...) {
  this.platform.ready().then(() => {
    this.ga.startTrackerWithId('UA-XXX').then(() => {
      this.app.viewDidEnter.subscribe((evt) => {
        // evt.instance is the Ionic page component
        this.ga.trackView(evt.instance.title);
      });
    }).catch(e => console.log('Doh', e));
  }
}
jean-baptiste
  • 674
  • 7
  • 18
5

1st thing Router is present in @angular/router module.

And for listening route change event you can place subscription on router changes object.

Code

class MyRouteEventClass {
  constructor(private router: Router) {
     router.changes.subscribe((val) => {
       /* Awesome code here */
     }
    )
  }
}
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
5

You can use plain old Javascript to be placed in app.component.ts. Assuming that you use Ionic2-RC0:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';

import { YourPage } from '../pages/yourpage/yourpage';

@Component({
  template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
  rootPage = YourPage;

  constructor(platform: Platform) {
    platform.ready().then(() => {

      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      StatusBar.styleDefault();
      window.addEventListener('load', () =>{
         console.log('page changed');
      });
    });
  }

Every time you change page using the NavController, you'll have page changed printed out in the console.

Mathieu Nls
  • 2,285
  • 2
  • 17
  • 32
  • 1
    I like this answer, but it looks like the load event is only firing once at the beginning, instead of each time a page changes. – jmilloy Oct 13 '16 at 15:02
  • 1
    You can use `focusout` instead of `load`. It'll work for each new page load. It won't work for NavController.pop, however. – Mathieu Nls Oct 13 '16 at 15:31
  • Great. I actually care about *leaving* pages currently, anyways, so this seems spot on. Are there other times that the `focusout` event will fire that I need to worry about? – jmilloy Oct 13 '16 at 17:53
  • If you apply it on `window`, no, I don't think so. I did a few try with Nav `pop`, `push`, `setRoot`. Maybe when opening opening external app, for example, if you use the `SocialSharing` Cordova plugin, `focusout` should be triggered when the native sharing menu pops up. – Mathieu Nls Oct 13 '16 at 17:57
  • Hmmm, I haven't dug too much, but it does seem to be firing other times. I just want it to stop playback of all audio and free audio resources when leaving a page, but focusout fires when I hit the play button! So all the audio is freed before it can play. – jmilloy Oct 13 '16 at 18:05
2

In Ionic2+ you can simply subscribe to the event you want to execute your code on as follows:

this.navCtrl.ionViewWillUnload.subscribe(view => {
    console.log(view);
});

You can subscribe to all the Lifecycle Events

Dhyey
  • 4,275
  • 3
  • 26
  • 33