diff --git a/ionic/components/nav/nav-item.js b/ionic/components/nav/nav-item.js index ff73a75970..ded6d8c086 100644 --- a/ionic/components/nav/nav-item.js +++ b/ionic/components/nav/nav-item.js @@ -1,11 +1,11 @@ import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref'; +import {ElementRef} from 'angular2/src/core/compiler/element_ref'; import {bind} from 'angular2/di'; import * as util from 'ionic/util'; import {NavController} from './nav-controller'; import {Nav} from './nav'; import {NavPane, NavPaneSection} from './nav'; -import {Lifecycle} from 'ionic/components/view/lifecycle'; export class NavItem { @@ -37,11 +37,10 @@ export class NavItem { } render() { - if (this.isRendered) { - console.log('showed existing view', this.id); + + if (this.instance) { return Promise.resolve(); } - this.isRendered = true; let resolve; let promise = new Promise((res) => { resolve = res; }); @@ -67,6 +66,9 @@ export class NavItem { let viewContainer = navPane.contentContainerRef; let hostViewRef = viewContainer.create(componentProtoViewRef, -1, null, injector); + let newLocation = new ElementRef(hostViewRef, 0); + this.instance = this.nav.loader._viewManager.getComponent(newLocation); + this.disposals.push(() => { viewContainer.remove( viewContainer.indexOf(hostViewRef) ); }); @@ -74,16 +76,25 @@ export class NavItem { this.viewEle = hostViewRef._view.render._view.rootNodes[0]; this.viewEle.classList.add('nav-item'); + let context = { + boundElementIndex: 0, + parentView: { + _view: hostViewRef._view.componentChildViews[0] + } + }; + // add only the sections it needs if (itemStructure.navbar) { let navbarViewContainer = navPane.sections.navbar.viewContainerRef; - this.navbarView = navbarViewContainer.create(this.protos.navbar, -1, null, injector); + this.navbarView = navbarViewContainer.create(this.protos.navbar, -1, context, injector); this.disposals.push(() => { navbarViewContainer.remove( navbarViewContainer.indexOf(this.navbarView) ); }); } + this.loaded(); + resolve(); }); @@ -103,69 +114,18 @@ export class NavItem { return itemStructure; } -// if (this.created) { -// console.log('showed existing view', this.id); -// return Promise.resolve(); -// } - -// this.created = true; - -// let resolve; -// let promise = new Promise((res) => { resolve = res; }); - -// let injector = this.nav.injector.resolveAndCreateChild([ -// bind(NavController).toValue(this.nav.navCtrl), -// bind(NavParams).toValue(new NavParams(this.params)), -// bind(NavItem).toValue(this) -// ]); - -// this.nav.loader.loadNextToExistingLocation(this.Component, this.nav.contentElementRef, injector).then((componentRef) => { -// let navbarContainer = this.nav.navbarContainerRef; - -// if (componentRef && componentRef.dispose && navbarContainer) { -// this.disposals.push(componentRef.dispose); - -// this.viewEle = componentRef.location.domElement; -// this.viewEle.classList.add('ion-view'); - -// if (this._navbarProto) { -// let context = { -// boundElementIndex: 0, -// parentView: { -// _view: componentRef.location.parentView._view.componentChildViews[0] -// } -// }; - -// let atIndex = -1; - -// this._navbarView = navbarContainer.create(this._navbarProto, atIndex, context, injector); - -// if (this._navbarView) { -// this.disposals.push(() => { -// navbarContainer.remove( navbarContainer.indexOf(this._navbarView) ); -// }); -// } -// } -// } - -// console.log('created view', this.id); -// resolve(); -// }); - -// return promise; - cache() { - console.log('cached view', this.id); + this.didCache(); } destroy() { - console.log('destroyed view', this.id); - for (let i = 0; i < this.disposals.length; i++) { this.disposals[i](); } + this.didUnload(); + // just to help prevent any possible memory leaks for (let name in this) { if (this.hasOwnProperty(name)) { @@ -174,11 +134,6 @@ export class NavItem { } } - navbarProto(navbarProtoView) { - console.log('nav-item navbarProto') - this._navbarProto = navbarProtoView; - } - viewElement() { return this.viewEle; } @@ -223,6 +178,76 @@ export class NavItem { return this._backBtn; } + + /* + The view has loaded. This event only happens once per view being + created. If a view leaves but is cached, then this will not + fire again on a subsequent viewing. This method is a good place + to put your setup code for the view; however, it is not the + recommended method to use when a view becomes active. + */ + loaded() { + this.instance && this.instance.viewLoaded && this.instance.viewLoaded(); + } + + /* + The view is about to enter and become the active view. + */ + willEnter() { + this.instance && this.instance.viewWillEnter && this.instance.viewWillEnter(); + } + + /* + The view has fully entered and is now the active view. This + will fire, whether it was the first load or loaded from the cache. + */ + didEnter() { + this.instance && this.instance.viewDidEnter && this.instance.viewDidEnter(); + } + + /* + The view has is about to leave and no longer be the active view. + */ + willLeave() { + this.instance && this.instance.viewWillLeave && this.instance.viewWillLeave(); + } + + /* + The view has finished leaving and is no longer the active view. This + will fire, whether it is cached or unloaded. + */ + didLeave() { + this.instance && this.instance.viewDidLeave && this.instance.viewDidLeave(); + } + + /* + The view is about to become cached. + */ + willCache() { + this.instance && this.instance.viewWillCache && this.instance.viewWillCache(); + } + + /* + The view is now cached. + */ + didCache() { + this.instance && this.instance.viewDidCache && this.instance.viewDidCache(); + } + + /* + The view is about to be destroyed and have its elements removed. + */ + willUnload() { + this.instance && this.instance.viewWillUnload && this.instance.viewWillUnload(); + } + + /* + The view has been destroyed and its elements have been removed. + */ + didUnload() { + this.instance && this.instance.viewDidUnload && this.instance.viewDidUnload(); + } + } export class NavParams { diff --git a/ionic/components/nav/nav-item.js.orig b/ionic/components/nav/nav-item.js.orig deleted file mode 100644 index 72d9a60a57..0000000000 --- a/ionic/components/nav/nav-item.js.orig +++ /dev/null @@ -1,244 +0,0 @@ -import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref'; -import {bind} from 'angular2/di'; - -import * as util from 'ionic/util'; -import {NavController} from './nav-controller'; -<<<<<<< HEAD -import {Nav} from './nav'; -import {NavPane, NavPaneSection} from './nav'; -======= -import {Lifecycle} from 'ionic/components/view/lifecycle'; ->>>>>>> origin/master - - -export class NavItem { - - constructor(nav, Component, params = {}) { - this.nav = nav; - this.Component = Component; - this.params = params; - this.id = util.nextUid(); - this._titleEle = undefined; - this._backBtn = undefined; - this.disposals = []; - - // if it's possible to go back from this nav item - this.enableBack = false; - - this.protos = {}; - } - - addProtoViewRef(name, protoViewRef) { - this.protos[name] = protoViewRef; - } - - stage() { - // update if it's possible to go back from this nav item - //this.enableBack = !!this.nav.getPrevious(this); - - return this.render();; - } - - render() { - if (this.isRendered) { - console.log('showed existing view', this.id); - return Promise.resolve(); - } - this.isRendered = true; - - let resolve; - let promise = new Promise((res) => { resolve = res; }); - - // compile the Component - this.nav.compiler.compileInHost(this.Component).then(componentProtoViewRef => { - -<<<<<<< HEAD - // figure out the sturcture of this Component - // does it have a navbar? Is it tabs? Should it not have a navbar or any toolbars? - let itemStructure = this.getStructure(componentProtoViewRef); -======= - this.nav.loader.loadNextToExistingLocation(this.Component, this.nav.contentElementRef, injector).then((componentRef) => { - - Lifecycle.viewLoaded(componentRef.instance); - - console.log('nav-item loadNextToExistingLocation', this.nav.contentElementRef); - let navbarContainer = this.nav.navbarContainerRef; ->>>>>>> origin/master - - // get the appropriate NavPane which this NavItem will fit into - this.nav.getNavPane(itemStructure).then(navPane => { - - // create a new injector just for this NavItem - let injector = this.nav.injector.resolveAndCreateChild([ - bind(NavController).toValue(this.nav.navCtrl), - bind(NavParams).toValue(new NavParams(this.params)), - bind(NavItem).toValue(this) - ]); - - // add the content of the view to the content area - let viewContainer = navPane.contentContainerRef; - let hostViewRef = viewContainer.create(componentProtoViewRef, -1, null, injector); - - this.disposals.push(() => { - viewContainer.remove( viewContainer.indexOf(hostViewRef) ); - }); - - this.viewEle = hostViewRef._view.render._view.rootNodes[0]; - this.viewEle.classList.add('nav-item'); - - // add only the sections it needs - if (itemStructure.navbar) { - let navbarViewContainer = navPane.sections.navbar.viewContainerRef; - this.navbarView = navbarViewContainer.create(this.protos.navbar, -1, null, injector); - - this.disposals.push(() => { - navbarViewContainer.remove( navbarViewContainer.indexOf(this.navbarView) ); - }); - } - - resolve(); - }); - - }); - - return promise; - } - - getStructure(componentProtoViewRef) { - // navbar - toolbar - toolbar - content - toolbar - tabbar - let itemStructure = { - navbar: true, - tabbar: false, - toolbars: [], - key: 'c' - }; - - return itemStructure; - } -// if (this.created) { -// console.log('showed existing view', this.id); -// return Promise.resolve(); -// } - -// this.created = true; - -// let resolve; -// let promise = new Promise((res) => { resolve = res; }); - -// let injector = this.nav.injector.resolveAndCreateChild([ -// bind(NavController).toValue(this.nav.navCtrl), -// bind(NavParams).toValue(new NavParams(this.params)), -// bind(NavItem).toValue(this) -// ]); - -// this.nav.loader.loadNextToExistingLocation(this.Component, this.nav.contentElementRef, injector).then((componentRef) => { -// let navbarContainer = this.nav.navbarContainerRef; - -// if (componentRef && componentRef.dispose && navbarContainer) { -// this.disposals.push(componentRef.dispose); - -// this.viewEle = componentRef.location.domElement; -// this.viewEle.classList.add('ion-view'); - -// if (this._navbarProto) { -// let context = { -// boundElementIndex: 0, -// parentView: { -// _view: componentRef.location.parentView._view.componentChildViews[0] -// } -// }; - -// let atIndex = -1; - -// this._navbarView = navbarContainer.create(this._navbarProto, atIndex, context, injector); - -// if (this._navbarView) { -// this.disposals.push(() => { -// navbarContainer.remove( navbarContainer.indexOf(this._navbarView) ); -// }); -// } -// } -// } - -// console.log('created view', this.id); -// resolve(); -// }); - -// return promise; - - - cache() { - console.log('cached view', this.id); - } - - destroy() { - console.log('destroyed view', this.id); - - for (let i = 0; i < this.disposals.length; i++) { - this.disposals[i](); - } - - // just to help prevent any possible memory leaks - for (let name in this) { - if (this.hasOwnProperty(name)) { - this[name] = null; - } - } - } - - navbarProto(navbarProtoView) { - console.log('nav-item navbarProto') - this._navbarProto = navbarProtoView; - } - - viewElement() { - return this.viewEle; - } - - navbarElement() { - if (this.navbarView && this.navbarView._view) { - return this.navbarView._view.render._view.rootNodes[0]; - } - } - - contentElement() { - return this.viewEle.querySelector('ion-content'); - } - - titleElement() { - if (this._titleEle === undefined) { - //let navbarElement = this.navbarElement(); - //if (navbarElement) { - // let titleEle = navbarElement.querySelector('ion-title'); - // if (titleEle) { - // this._titleEle = titleEle; - // return this._titleEle; - // } - //} - this._titleEle = null; - } - return this._titleEle; - } - - backButtonElement() { - if (this._backBtn === undefined) { - let navbarElement = this.navbarElement(); - if (navbarElement) { - let backBtn = navbarElement.querySelector('back-button'); - if (backBtn) { - this._backBtn = backBtn; - return this._backBtn; - } - } - this._backBtn = null; - } - return this._backBtn; - } - -} - -export class NavParams { - constructor(params) { - util.extend(this, params); - } -} diff --git a/ionic/components/nav/nav.js b/ionic/components/nav/nav.js index 8713499e9a..db98e31312 100644 --- a/ionic/components/nav/nav.js +++ b/ionic/components/nav/nav.js @@ -135,15 +135,16 @@ export class Nav { } // the active item is going to be the leaving one (if one exists) - let leavingItem = this.getActive() || {}; + let leavingItem = this.getActive() || new NavItem(); leavingItem.shouldDestroy = false; + leavingItem.shouldCache = true; + leavingItem.willCache(); // create a new NavStackItem let enteringItem = new NavItem(this, Component, params); // set that this item is staged (it's not ready to be animated in yet) enteringItem.state = STAGED_STATE; - enteringItem.shouldDestroy = false; // add the item to the stack this.items.push(enteringItem); @@ -173,21 +174,22 @@ export class Nav { // get the active item and set that it is staged to be leaving // was probably the one popped from the stack - let leavingItem = this.getActive(); + let leavingItem = this.getActive() || new NavItem(); leavingItem.shouldDestroy = true; + leavingItem.shouldCache = false; + leavingItem.willUnload(); // the entering item is now the new last item // Note: we might not have an entering item if this is the only // item on the history stack. let enteringItem = this.getPrevious(leavingItem); if(enteringItem) { - enteringItem.shouldDestroy = false; - // start the transition this.transition(enteringItem, leavingItem, opts).then(() => { // transition completed, destroy the leaving item resolve(); }); + } else { this.transitionComplete(); resolve(); @@ -200,13 +202,18 @@ export class Nav { let resolve; let promise = new Promise(res => { resolve = res; }); - opts.isAnimated = opts.animation !== 'none'; + opts.isAnimated = (opts.animation !== 'none'); this.transitionStart(opts); // wait for the new item to complete setup enteringItem.stage().then(() => { + enteringItem.shouldDestroy = false; + enteringItem.shouldCache = false; + enteringItem.willEnter(); + leavingItem.willLeave(); + // set that the leaving item is stage to be leaving leavingItem.state = STAGED_LEAVING_STATE; @@ -234,6 +241,9 @@ export class Nav { // dispose any items that shouldn't stay around transAnimation.dispose(); + enteringItem.didEnter(); + leavingItem.didLeave(); + // all done! this.transitionComplete(); @@ -263,12 +273,17 @@ export class Nav { // get the active item and set that it is staged to be leaving // was probably the one popped from the stack - let leavingItem = this.getActive(); + let leavingItem = this.getActive() || new NavItem(); leavingItem.shouldDestroy = true; + leavingItem.shouldCache = false; + leavingItem.willLeave(); + leavingItem.willUnload(); // the entering item is now the new last item let enteringItem = this.getPrevious(leavingItem); enteringItem.shouldDestroy = false; + enteringItem.shouldCache = false; + enteringItem.willEnter(); // start the transition this.transitionStart({ isAnimated: true }); @@ -303,11 +318,18 @@ export class Nav { enteringItem.state = ACTIVE_STATE; leavingItem.state = CACHED_STATE; + enteringItem.didEnter(); + leavingItem.didLeave(); + } else { // cancelled the swipe back, return items to original state leavingItem.state = ACTIVE_STATE; enteringItem.state = CACHED_STATE; + leavingItem.willEnter(); + leavingItem.didEnter(); + enteringItem.didLeave(); + leavingItem.shouldDestroy = false; enteringItem.shouldDestroy = false; } @@ -371,8 +393,9 @@ export class Nav { this.remove(item); item.destroy(); - } else if(item.state !== ACTIVE_STATE) { + } else if (item.state === CACHED_STATE && item.shouldCache) { item.cache(); + item.shouldCache = false; } } }); diff --git a/ionic/components/nav/test/basic/pages/first-page.js b/ionic/components/nav/test/basic/pages/first-page.js index 68b2c7581d..6869a83967 100644 --- a/ionic/components/nav/test/basic/pages/first-page.js +++ b/ionic/components/nav/test/basic/pages/first-page.js @@ -9,7 +9,7 @@ import {SecondPage} from './second-page'; @View({ template: '' + '' + - 'First Page' + + 'First Page: {{ val }}' + '' + '' + '' + @@ -35,11 +35,39 @@ export class FirstPage { } viewLoaded() { - console.log('VIEW LOADED'); + console.log('viewLoaded first page'); } - viewWillShow() { - console.log('VIEW WILL SHOW'); + viewWillEnter() { + console.log('viewWillEnter first page'); + } + + viewDidEnter() { + console.log('viewDidEnter first page'); + } + + viewWillLeave() { + console.log('viewWillLeave first page'); + } + + viewDidLeave() { + console.log('viewDidLeave first page'); + } + + viewWillCache() { + console.log('viewWillCache first page'); + } + + viewDidCache() { + console.log('viewDidCache first page'); + } + + viewWillUnload() { + console.log('viewWillUnload first page'); + } + + viewDidUnload() { + console.log('viewDidUnload first page'); } push() { diff --git a/ionic/components/nav/test/basic/pages/second-page.js b/ionic/components/nav/test/basic/pages/second-page.js index c56c30f14f..0db33d5cf6 100644 --- a/ionic/components/nav/test/basic/pages/second-page.js +++ b/ionic/components/nav/test/basic/pages/second-page.js @@ -46,4 +46,40 @@ export class SecondPage { this.nav.push(ThirdPage); } + viewLoaded() { + console.log('viewLoaded second page'); + } + + viewWillEnter() { + console.log('viewWillEnter second page'); + } + + viewDidEnter() { + console.log('viewDidEnter second page'); + } + + viewWillLeave() { + console.log('viewWillLeave second page'); + } + + viewDidLeave() { + console.log('viewDidLeave second page'); + } + + viewWillCache() { + console.log('viewWillCache second page'); + } + + viewDidCache() { + console.log('viewDidCache second page'); + } + + viewWillUnload() { + console.log('viewWillUnload second page'); + } + + viewDidUnload() { + console.log('viewDidUnload second page'); + } + } diff --git a/ionic/components/nav/test/basic/pages/third-page.js b/ionic/components/nav/test/basic/pages/third-page.js index 19a15f911b..44af44c837 100644 --- a/ionic/components/nav/test/basic/pages/third-page.js +++ b/ionic/components/nav/test/basic/pages/third-page.js @@ -28,7 +28,45 @@ export class ThirdPage { ) { this.nav = nav } + pop() { this.nav.pop() } + + viewLoaded() { + console.log('viewLoaded third page'); + } + + viewWillEnter() { + console.log('viewWillEnter third page'); + } + + viewDidEnter() { + console.log('viewDidEnter third page'); + } + + viewWillLeave() { + console.log('viewWillLeave third page'); + } + + viewDidLeave() { + console.log('viewDidLeave third page'); + } + + viewWillCache() { + console.log('viewWillCache third page'); + } + + viewDidCache() { + console.log('viewDidCache third page'); + } + + viewWillUnload() { + console.log('viewWillUnload third page'); + } + + viewDidUnload() { + console.log('viewDidUnload third page'); + } + } diff --git a/ionic/components/view/lifecycle.js b/ionic/components/view/lifecycle.js deleted file mode 100644 index 158461c24f..0000000000 --- a/ionic/components/view/lifecycle.js +++ /dev/null @@ -1,14 +0,0 @@ -export class Lifecycle { - static viewLoaded(component) { - component.viewLoaded && component.viewLoaded(); - } - static viewWillShow(component) { - component.viewWillShow && component.viewWillShow(); - } - static viewEntered(component) { - component.viewEntered && component.viewEntered(); - } - static viewDestroyed(component) { - component.viewDestroyed && component.viewDestroyed(); - } -}