ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
50.94k stars 13.51k forks source link

Ionic page constructors are executing multiple times #5960

Closed nikiben closed 8 years ago

nikiben commented 8 years ago

Short description of the problem:

The constructor execute when user navigates from page1 to page2 and then back to page1 (the second time), so the constructor executes every time user navigates to different page that has already been loaded. The configurations, models and variables are been reset or re-initialized.

What behavior are you expecting?

I was expecting the constructor to init just once when the page loads, not every time user navigates to already loaded page.

Steps to reproduce:

  1. create new ionic v2 project
  2. create 2 pages. Page1 and Page2
  3. Log a sample message in the constructor like below in Page1.js and Page2.js
  4. Navigate to Page2 and then back to Page1. Inspect the console and you'll notice that the message "My Constructor has been initialized....." is printed to the console everytime
    constructor(nav) {
        this.nav = nav;
        **console.log("My Constructor has been initialized.....");**
    }

Other information: (e.g. stacktraces, related issues, suggestions how to fix, stackoverflow links, forum links, etc)

Which Ionic Version? 1.x or 2.x $projectfolder>ionic -v Ionic 2.0.0-beta.19

Run ionic info from terminal/cmd prompt: (paste output below)

Your system information:

Cordova CLI: 5.4.1 Gulp version: CLI version 3.9.0 Gulp local: Local version 3.9.1 Ionic Version: 2.0.0-beta.3 Ionic CLI Version: 2.0.0-beta.19 Ionic App Lib Version: 2.0.0-beta.9 OS: Node Version: v5.4.1

jgw96 commented 8 years ago

Hello! Thanks for opening an issue with us! I am having trouble reproducing this issue with the exact steps you listed above. Would you be able to maybe give me a full sample of the code your having this issue with? Thanks again!

nikiben commented 8 years ago

See below sample code... You can also download the full code from my webspace cutePuppyPics.rar

App.js

import 'es6-shim';
import {App, IonicApp, Platform, MenuController} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
import {Page4Page} from './pages/page-4/page-4';
import {Page5Page} from './pages/page-5/page-5';

@App({
  templateUrl: './build/app.html',
  config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
  static get parameters() {
    return [[IonicApp], [Platform], [MenuController]];
  }

  constructor(app, platform, menu) {
    this.rootPage = TabsPage;
    this.app = app;

    this.page4 = Page4Page;
    this.page5 = Page5Page;

    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();
    });
  }

  openPage(page) {
      this.app.getComponent('menu').close();
      let nav = this.app.getComponent('nav');
      nav.setRoot(page);
  }
}

`

First Page

import {Page, NavController} from 'ionic-angular';

/*
  Generated class for the Page4Page page.

  See http://ionicframework.com/docs/v2/components/#navigation for more info on
  Ionic pages and navigation.
*/
@Page({
  templateUrl: 'build/pages/page-4/page-4.html',
})
export class Page4Page {
  static get parameters() {
    return [[NavController]];
  }

  constructor(nav) {
    this.nav = nav;
    console.log("Initiating Page4....");
  }
}

`

Second Page

import {Page, NavController} from 'ionic-angular';

/*
  Generated class for the Page5Page page.

  See http://ionicframework.com/docs/v2/components/#navigation for more info on
  Ionic pages and navigation.
*/
@Page({
  templateUrl: 'build/pages/page-5/page-5.html',
})
export class Page5Page {
  static get parameters() {
    return [[NavController]];
  }

  constructor(nav) {
    this.nav = nav;
    console.log("Initiating Page 5....");
  }
}

`

image

nikiben commented 8 years ago

Sorry, forgot to mention, the navigation is from the menu. I.e navigate to page2 from menu and back to page1 from the menu.

Thanks

nikiben commented 8 years ago

Tested on Device... Same issue

nikiben commented 8 years ago

Hello, can you please advise on this? Thanks

jgw96 commented 8 years ago

Hello! Sorry it has taken so long to get back to you. So i made a plunker (http://plnkr.co/edit/mJQG4dy3uqaZlQUQfvFZ) for you to check out that i think might help with some of the theory behind this issue. So you will see from my plunker that an alert is set in the constructor of the home page (the first page you see) and the page that is navigated too when you hit the thanks! button (page 1). So if you test this plunker you will see that the constructor for the home page is only initialized once, you only see the home page alert when the app is first loaded. Now you will notice that it does alert every time page 1 is navigated too, this is expected behavior. When you click that back button on page 1 that navigates you back to the home page, it destroys that instance of the page 1 page, therefore when it is navigated to again it has to be constructed again. So to sum all this up, your root page (the page your app starts on usually) will never have to be constructed except when the app first loads, but any page that you push and then close will have to be constructed ever time it is navigated to.

So with a sidemenu app such as yours, where your using setRoot every time you navigate to a page from the menu is gonna "reset" that page every time it is navigated too because its setting it as root, therefore causing it to be constructed every time it is navigated too. This is expected behavior and should not cause any issues with your app.

I hope i explained all this well! Thanks for using Ionic! I am going to close this issue for now as i believe the above should clear it up, but feel free to comment still!

nikiben commented 8 years ago

Hi Justin,

So I modified the plnkr and I tried putting the code below in Page1. Seems like the main starting page (root Page) still gets re-constructed.

import { IonicApp, Page, NavController } from 'ionic-angular/index'; import { HomePage } from './home.ts';

@Page({

}

Ebenezer Isaac Director | Entrepreneur Ben-Eris Technology Inc. C: (647) 704-3823 www.beneristechnology.com

Confidentiality Notice: This e-mail, including any attachments, is for the sole use of the intended recipient(s) and may contain private, confidential, and privileged information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient or this information has been inappropriately forwarded to you, please contact the sender by reply e-mail and destroy all copies of the original.

On Thu, Apr 28, 2016 at 11:41 AM, Justin Willis notifications@github.com wrote:

Hello! Sorry it has taken so long to get back to you. So i made a plunker ( http://plnkr.co/edit/mJQG4dy3uqaZlQUQfvFZ) for you to check out that i think might help with some of the theory behind this issue. So you will see from my plunker that an alert is set in the constructor of the home page (the first page you see) and the page that is navigated too when you hit the thanks! button (page 1). So if you test this plunker you will see that the constructor for the home page is only initialized once, you only see the home page alert when the app is first loaded. Now you will notice that it does alert every time page 1 is navigated too, this is expected behavior. When you click that back button on page 1 that navigates you back to the home page, it destroys that instance of the page 1 page, therefore when it is navigated to again it has to be constructed again. So to sum all this up, your root page (the page your app star ts on us ually) will never have to be constructed except when the app first loads, but any page that you push and then close will have to be constructed ever time it is navigated to.

So with a sidemenu app such as yours, where your using setRoot every time you navigate to a page from the menu is gonna "reset" that page every time it is navigated too because its setting it as root, therefore causing it to be constructed every time it is navigated too. This is expected behavior and should not cause any issues with your app.

I hope i explained all this well! Thanks for using Ionic! I am going to close this issue for now as i believe the above should clear it up, but feel free to comment still!

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/driftyco/ionic/issues/5960#issuecomment-215470841

jgw96 commented 8 years ago

Hey! So yeah, since your doing a setRoot on the home page it is going to be reconstructed when you navigate to it because its being reset as the root and has to be reinitialized.

nikiben commented 8 years ago

Thanks Justin,

Is there a documentation that shows how to navigate through the app without reconstruction every time?

How would one achieve this?

Ben On Apr 28, 2016 4:12 PM, "Justin Willis" notifications@github.com wrote:

Hey! So yeah, since your doing a setRoot on the home page it is going to be reconstructed when you navigate to it because its being reset as the root and has to be reinitialized.

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/driftyco/ionic/issues/5960#issuecomment-215547590

jgw96 commented 8 years ago

@nikiben so if your on your root page and you navigate to a different view, then to get back to your root page all you have to do is use nav.pop(rootPage). There is no need to set it as root again before you navigate back to it. This will keep it from reconstructing (: .

nikiben commented 8 years ago

@jgw96 I notice this happens as well with the Ionic menu starter tutorial project https://github.com/driftyco/ionic2-starter-tutorial. The page get's reconstructed every time user clicked on menu items. But this is different in Ionic 1.

My concern is that if I have an http call or if i'm loading some sort of data on a pop and push page that means i will be loading the resource(s) everytime I visit that page (since the instance will be destroyed). I just want to load the data once until it's refreshed by user.

Is there a way to workaround this?

paulsson commented 8 years ago

@jgw96 is there a reason that NavController setRoot can't cache other root pages that have been used and replaced by other pages? I agree with @nikiben that it is not ideal to have to recreate a page (along with model data, data retrieval, etc) every time it is set as root on the NavController. Can an option be added to cache a root page even when it is removed so the next time it is set as the root page it can just start at its last state?

deepakkumarsharma-tudip commented 7 years ago

hmm

wodka commented 7 years ago

I'm having a similar issue but I call setRoot() only once!

the component looks like this:

export class Chats {
    constructor() {
        console.log('start chat component!', new Error);
    }
}

these are the logs even though there is only one this.nav.setRoot(Chats); call. The Constructor is called twice, once at NavControllerBase._setPages and once at NavControllerBase._trnsFinish

start chat component! Error
    at new Chats (MyApp/src/pages/chats/chats.ts:32:46)
    at new Wrapper_Chats (/AppModule/Chats/wrapper.ngfactory.js:7:18)
    at _View_Chats_Host0.createInternal (/AppModule/Chats/host.ngfactory.js:16:21)
    at _View_Chats_Host0.AppView.create (MyApp/node_modules/@angular/core/src/linker/view.js:98:21)
    at _View_Chats_Host0.DebugAppView.create (MyApp/node_modules/@angular/core/src/linker/view.js:308:44)
    at ComponentFactory.create (MyApp/node_modules/@angular/core/src/linker/component_factory.js:157:36)
    at NavControllerBase._viewInit (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:333:44)
    at NavControllerBase._nextTrns (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:205:18)
    at NavControllerBase._queueTrns (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:189:14)
    at NavControllerBase._setPages (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:131:21)
start chat component! Error
    at new Chats (MyApp/src/pages/chats/chats.ts:32:46)
    at new Wrapper_Chats (/AppModule/Chats/wrapper.ngfactory.js:7:18)
    at _View_Chats_Host0.createInternal (/AppModule/Chats/host.ngfactory.js:16:21)
    at _View_Chats_Host0.AppView.create (MyApp/node_modules/@angular/core/src/linker/view.js:98:21)
    at _View_Chats_Host0.DebugAppView.create (MyApp/node_modules/@angular/core/src/linker/view.js:308:44)
    at ComponentFactory.create (MyApp/node_modules/@angular/core/src/linker/component_factory.js:157:36)
    at NavControllerBase._viewInit (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:333:44)
    at NavControllerBase._nextTrns (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:205:18)
    at ti.resolve (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:156:19)
    at NavControllerBase._trnsFinish (MyApp/node_modules/ionic-angular/navigation/nav-controller-base.js:500:9)
KarimMesallam commented 7 years ago

@jgw96 I have a similar issue when I logout then login again to my app.

I use setRoot when logging in, and setRoot when logging out. I've tried push when logging in (which is not a good practice since it keeps the login page in the stack) but it still didn't fix the issue.

Whenever I logout and login again, the constructor of tab1Root increments in execution.

In the first time, I only login, and the constructor run once normally, then after a logout and a login, it runs twice, then another logout and login and it runs thrice, and so on...

The only workaround I can think of is using window.location.reload() after logging out, but I can't do this in the app since it will break the native feel and seem buggy.

pittdewaard commented 7 years ago

Hi, I'm also use setRoot(page) to navigate between my pages, in some of my pages I subscribe to an observer, after while of navigation for and back I have several subscriptions to the same observer, even I can unsubscribe, I need two subscriptions over the complete app livecycle, without having multiple subscriptions, especially because after a while nodejs/events.js runs in memoryleaks!

some ideas?

KR

Pitt

jeanbaptistevilain commented 7 years ago

Hi @jgw96, Same problem as @KarimMesallam here. We're using setRoot to init the navigation stack after a login or a logout, and the post-login controller is initialized 3 times in a row after a login - logout - login cycle. Therefore ionViewDidEnter is also executed 3 times, which produces unexpected behavior in our app. Using ionViewDidLoad instead did not make any difference, FWIW. Can you please advise ? I could not find a workaround for this one, and it has a major impact on the navigation in our app.

Thanks in advance

emgould commented 7 years ago

Same problem. Using setRoot based on auth state. constructors running multiple times if I use tabs.

megharajdeepak commented 7 years ago

Though it is intended behaviour right now, I reckon ideally there should be an option for the developer whether to retain the page\view as-is in memory or whether he\she wants to reload it every time he\she land up on the page.

A property on ionic page like 'destructionPolicy' which can take values as 'auto' or 'never' would help. That's just me though ;)

Thanks for ionic2... Cheers!

wodka commented 7 years ago

@megharajdeepak I do not thing it is intended that when you call setRoot once it will trigger the construct multiple times.

chanphillip commented 7 years ago

Just passing by, try wrapping zone.run to setRoot I had the same issue and this workaround is still working properly.

See more about this: https://forum.ionicframework.com/t/after-setroot-tabspage-the-default-tab-page-appears-twice/71770/10

samarthagarwal commented 7 years ago

Going back and forth between pages seems to create multiple copies of the pages, each one active behind the scenes, even if the parameters are different. I am using socket.io, and receiving the same message multiple times on the messages page. If I go back and come back to the messages page, the new message will show up one more time than the last time.

jeanbaptistevilain commented 7 years ago

@chanphillip many thanks for passing by ! Your suggestion worked perfectly, root pages aren't duplicated anymore. I wish we had a proper explanation as to why this happens, I'm still a bit puzzled myself but it's good to know that we've got a workaround. Cheers !

ProlificBlueprint commented 6 years ago

I seemed to run into this problem when implementing links in the url

// caused my controllers to be loaded twice IonicModule.forRoot( Spotlight , { mode: 'ios' }, { links: app_links }),

// only loaded once as expected IonicModule.forRoot( Spotlight , { mode: 'ios' }),

something happening with Ionic taking over routing .

AhmetFUsta commented 6 years ago

I am still facing this issue, also in a login - logout cycle where the contructor (and ngOnInit) are executed multiple times (actually the entire page is rendered multiple times) and the amount of times keeps increasing everytime the cycle happens, it resets when the app or page is reloaded. So basically the same thing as people mentioned before me. I tried the NgZone approach and it did not fix/bypass this issue. Do you know anything about this @jgw96 ?

unnikrishnan-ateam commented 6 years ago

I am also facing the same issue. Is there any workarounds? I tried the method mentioned by @chanphillip but unfortunately it's not working for me. If there are any other workarounds or methods to fix this please share here. This issue is killing my app navigation.

jeffersoncostas commented 6 years ago

I have the same issue, the @chanphillip workaround its not working :/

bhumin3i commented 6 years ago

i have same issue any one have solution?

gurnard123 commented 6 years ago

I have the same issue. This has been opened for more than 2 years and this is a common piece of funcitonality, login/logout. How should this be performed so that multiple observers are not created. subscription to platform.resume is created each time a page is loaded.

DBLTecnologia commented 6 years ago

Same here, any solutions?

ionitron-bot[bot] commented 6 years ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.