NavItem lifecycle

This commit is contained in:
Adam Bradley
2015-06-02 21:34:25 -05:00
parent 2e6fb5cd04
commit 8c0a6277fe
7 changed files with 226 additions and 334 deletions

View File

@@ -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 {

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ import {SecondPage} from './second-page';
@View({
template: '' +
'<ion-navbar *navbar>' +
'<ion-title>First Page</ion-title>' +
'<ion-title>First Page: {{ val }}</ion-title>' +
'<ion-nav-items primary>' +
'<button class="button">P1</button>' +
'</ion-nav-items>' +
@@ -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() {

View File

@@ -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');
}
}

View File

@@ -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');
}
}

View File

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