From 033e1eae174327fdf3a6a71e6101e21395e85dba Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 1 Nov 2016 19:12:29 +0100 Subject: [PATCH] fix(nav): adds public willLoad lifecycle event * fix(nav): adds public willLoad lifecycle event * test(menu): adds more asserts --- src/components/menu/menu.ts | 15 +- src/components/modal/modal-component.ts | 2 +- src/components/modal/test/basic/app-module.ts | 14 ++ src/components/nav/test/basic/app-module.ts | 32 +++-- src/components/picker/picker-component.ts | 2 +- src/components/popover/popover-component.ts | 2 +- .../tabs/test/lifecyles/app-module.ts | 129 ++++++++++++++++++ src/navigation/nav-controller-base.ts | 16 ++- src/navigation/view-controller.ts | 9 ++ 9 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 src/components/tabs/test/lifecyles/app-module.ts diff --git a/src/components/menu/menu.ts b/src/components/menu/menu.ts index 2c620f6b2d3..14a2e4b63e1 100644 --- a/src/components/menu/menu.ts +++ b/src/components/menu/menu.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmit import { Backdrop } from '../backdrop/backdrop'; import { Config } from '../../config/config'; -import { isTrueProperty } from '../../util/util'; +import { isTrueProperty, assert } from '../../util/util'; import { Keyboard } from '../../util/keyboard'; import { MenuContentGesture } from './menu-gestures'; import { MenuController } from './menu-controller'; @@ -192,7 +192,6 @@ export class Menu { private _cntEle: HTMLElement; private _cntGesture: MenuContentGesture; private _type: MenuType; - private _resizeUnreg: Function; private _isEnabled: boolean = true; private _isSwipeEnabled: boolean = true; private _isAnimating: boolean = false; @@ -472,6 +471,8 @@ export class Menu { } private _before() { + assert(this._isAnimating === false, '_before should be called when we are not animating'); + // this places the menu into the correct location before it animates in // this css class doesn't actually kick off any animations this.menuContent && this.menuContent.resize(); @@ -482,6 +483,8 @@ export class Menu { } private _after(isOpen: boolean) { + assert(this._isAnimating === true, '_after should be called when we are animating'); + // keep opening/closing the menu disabled for a touch more yet // only add listeners/css if it's enabled and isOpen // and only remove listeners/css if it's not open @@ -516,21 +519,21 @@ export class Menu { /** * @private */ - open() { + open(): Promise { return this.setOpen(true); } /** * @private */ - close() { + close(): Promise { return this.setOpen(false); } /** * @private */ - toggle() { + toggle(): Promise { return this.setOpen(!this.isOpen); } @@ -633,12 +636,10 @@ export class Menu { this._events.unlistenAll(); this._cntGesture && this._cntGesture.destroy(); this._type && this._type.destroy(); - this._resizeUnreg && this._resizeUnreg(); this._cntGesture = null; this._type = null; this._cntEle = null; - this._resizeUnreg = null; } } diff --git a/src/components/modal/modal-component.ts b/src/components/modal/modal-component.ts index 5df724f0e32..642646a83e9 100644 --- a/src/components/modal/modal-component.ts +++ b/src/components/modal/modal-component.ts @@ -30,7 +30,7 @@ export class ModalCmp { this._bdDismiss = _navParams.data.opts.enableBackdropDismiss; } - ionViewWillLoad() { + ionViewPreLoad() { this._load(this._navParams.data.component); } diff --git a/src/components/modal/test/basic/app-module.ts b/src/components/modal/test/basic/app-module.ts index ebc874a2106..98df450c8ca 100644 --- a/src/components/modal/test/basic/app-module.ts +++ b/src/components/modal/test/basic/app-module.ts @@ -149,6 +149,7 @@ export class E2EPage {

ionViewCanEnter ({{called.ionViewCanEnter}})

ionViewCanLeave ({{called.ionViewCanLeave}})

+

ionViewWillLoad ({{called.ionViewWillLoad}})

ionViewDidLoad ({{called.ionViewDidLoad}})

ionViewWillEnter ({{called.ionViewWillEnter}})

ionViewDidEnter ({{called.ionViewDidEnter}})

@@ -178,6 +179,7 @@ export class ModalPassData { this.called = { ionViewCanEnter: 0, ionViewCanLeave: 0, + ionViewWillLoad: 0, ionViewDidLoad: 0, ionViewWillEnter: 0, ionViewDidEnter: 0, @@ -213,6 +215,11 @@ export class ModalPassData { }); } + ionViewWillLoad() { + console.log('ModalPassData ionViewWillLoad fired'); + this.called.ionViewWillLoad++; + } + ionViewDidLoad() { console.log('ModalPassData ionViewDidLoad fired'); this.called.ionViewDidLoad++; @@ -400,6 +407,7 @@ export class ContactUs {

ionViewCanEnter ({{called.ionViewCanEnter}})

ionViewCanLeave ({{called.ionViewCanLeave}})

+

ionViewWillLoad ({{called.ionViewWillLoad}})

ionViewDidLoad ({{called.ionViewDidLoad}})

ionViewWillEnter ({{called.ionViewWillEnter}})

ionViewDidEnter ({{called.ionViewDidEnter}})

@@ -441,6 +449,7 @@ export class ModalFirstPage { this.called = { ionViewCanEnter: 0, ionViewCanLeave: 0, + ionViewWillLoad: 0, ionViewDidLoad: 0, ionViewWillEnter: 0, ionViewDidEnter: 0, @@ -479,6 +488,11 @@ export class ModalFirstPage { return true; } + ionViewWillLoad() { + console.log('ModalFirstPage ionViewWillLoad fired'); + this.called.ionViewWillLoad++; + } + ionViewDidLoad() { console.log('ModalFirstPage ionViewDidLoad fired'); this.called.ionViewDidLoad++; diff --git a/src/components/nav/test/basic/app-module.ts b/src/components/nav/test/basic/app-module.ts index ee324e5582f..35e719dc0fa 100644 --- a/src/components/nav/test/basic/app-module.ts +++ b/src/components/nav/test/basic/app-module.ts @@ -1,15 +1,22 @@ import { NgModule, Component, ViewChild } from '@angular/core'; -import { App, AlertController, Content, DeepLinkConfig, IonicApp, IonicModule, Label, NavController, NavParams, Tabs, Tab, ModalController, ViewController } from '../../../..'; +import { App, AlertController, Content, DeepLinkConfig, IonicApp, IonicModule, NavController, NavParams, Tabs, Tab, ModalController, ViewController } from '../../../..'; + +@Component({ + selector: 'my-cmp2', + template: `{{value}}` +}) +export class MyCmpTest2 { + value: string = 'Test Failed'; +} @Component({ selector: 'my-cmp', - template: `My Custom Component Test - {{value}}` + template: ` {{value}}` }) export class MyCmpTest { - @ViewChild(Label) _label: Label; - label: Label; - value: string = ''; + @ViewChild(MyCmpTest2) _label: MyCmpTest2; + label: MyCmpTest2; + value: string = 'Test Failed'; ngOnInit() { this.label = this._label; @@ -34,6 +41,7 @@ export class MyCmpTest {

ionViewCanEnter ({{called.ionViewCanEnter}})

ionViewCanLeave ({{called.ionViewCanLeave}})

+

ionViewWillLoad ({{called.ionViewWillLoad}})

ionViewDidLoad ({{called.ionViewDidLoad}})

ionViewWillEnter ({{called.ionViewWillEnter}})

ionViewDidEnter ({{called.ionViewDidEnter}})

@@ -91,6 +99,7 @@ export class FirstPage { this.called = { ionViewCanEnter: 0, ionViewCanLeave: 0, + ionViewWillLoad: 0, ionViewDidLoad: 0, ionViewWillEnter: 0, ionViewDidEnter: 0, @@ -104,11 +113,11 @@ export class FirstPage { for (var i = 1; i <= 50; i++) { this.pages.push(i); } - // if (!this.myCmp || !this.content || !this.myCmp.label) { - // throw new Error('children are not loaded'); - // } - this.myCmp.value = 'root!'; - // this.myCmp.label.color = 'primary'; + if (!this.myCmp || !this.content || !this.myCmp.label) { + throw new Error('children are not loaded'); + } + this.myCmp.value = '👍 self test passed!'; + this.myCmp.label.value = '👍 children test passed!'; this.called.ionViewDidLoad++; } @@ -810,6 +819,7 @@ export const deepLinkConfig: DeepLinkConfig = { RedirectPage, AnotherPage, MyCmpTest, + MyCmpTest2, FullPage, PrimaryHeaderPage, TabsPage, diff --git a/src/components/picker/picker-component.ts b/src/components/picker/picker-component.ts index 939e108316d..baa9ea96045 100644 --- a/src/components/picker/picker-component.ts +++ b/src/components/picker/picker-component.ts @@ -423,7 +423,7 @@ export class PickerCmp { this.lastClick = 0; } - ionViewDidLoad() { + ionViewWillLoad() { // normalize the data let data = this.d; diff --git a/src/components/popover/popover-component.ts b/src/components/popover/popover-component.ts index 22118ed2cf8..47cb678b9dc 100644 --- a/src/components/popover/popover-component.ts +++ b/src/components/popover/popover-component.ts @@ -60,7 +60,7 @@ export class PopoverCmp { this.id = (++popoverIds); } - ionViewWillLoad() { + ionViewPreLoad() { let activeElement: any = document.activeElement; if (document.activeElement) { activeElement.blur(); diff --git a/src/components/tabs/test/lifecyles/app-module.ts b/src/components/tabs/test/lifecyles/app-module.ts new file mode 100644 index 00000000000..5934637cd7f --- /dev/null +++ b/src/components/tabs/test/lifecyles/app-module.ts @@ -0,0 +1,129 @@ +import { Component, NgModule } from '@angular/core'; +import { IonicApp, IonicModule, NavController, AlertController } from '../../../..'; + +// +// Tab 1 +// +@Component({ + template: ` + + + Lifecyles + + + + +

ionViewCanEnter ({{called.ionViewCanEnter}})

+

ionViewCanLeave ({{called.ionViewCanLeave}})

+

ionViewWillLoad ({{called.ionViewWillLoad}})

+

ionViewDidLoad ({{called.ionViewDidLoad}})

+

ionViewWillEnter ({{called.ionViewWillEnter}})

+

ionViewDidEnter ({{called.ionViewDidEnter}})

+

ionViewWillLeave ({{called.ionViewWillLeave}})

+

ionViewDidLeave ({{called.ionViewDidLeave}})

+ + + +
+ ` +}) +export class Tab1 { + called: any; + + constructor(private alertCtrl: AlertController, private navCtrl: NavController) { + this.called = { + ionViewCanEnter: 0, + ionViewCanLeave: 0, + ionViewWillLoad: 0, + ionViewDidLoad: 0, + ionViewWillEnter: 0, + ionViewDidEnter: 0, + ionViewWillLeave: 0, + ionViewDidLeave: 0 + }; + } + + push() { + this.navCtrl.push(Tab1); + } + + openAlert() { + this.alertCtrl.create({ + title: 'Example' + }).present(); + } + + ionViewCanEnter() { + this.called.ionViewCanEnter++; + return true; + } + + ionViewCanLeave() { + this.called.ionViewCanLeave++; + return true; + } + + ionViewWillLoad() { + this.called.ionViewWillLoad++; + } + + ionViewDidLoad() { + this.called.ionViewDidLoad++; + } + + ionViewWillEnter() { + this.called.ionViewWillEnter++; + } + + ionViewDidEnter() { + this.called.ionViewDidEnter++; + } + + ionViewWillLeave() { + this.called.ionViewWillLeave++; + } + + ionViewDidLeave() { + this.called.ionViewDidLeave++; + } +} + +@Component({ + template: ` + + + + + + ` +}) +export class TabsPage { + root = Tab1; +} + +@Component({ + template: `` +}) +export class E2EApp { + root = TabsPage; +} + +@NgModule({ + declarations: [ + E2EApp, + Tab1, + TabsPage + ], + imports: [ + IonicModule.forRoot(E2EApp, { + tabsHighlight: true, + }) + ], + bootstrap: [IonicApp], + entryComponents: [ + E2EApp, + Tab1, + TabsPage + ] +}) +export class AppModule {} diff --git a/src/navigation/nav-controller-base.ts b/src/navigation/nav-controller-base.ts index d72ac34597c..af0e38713cf 100644 --- a/src/navigation/nav-controller-base.ts +++ b/src/navigation/nav-controller-base.ts @@ -419,12 +419,15 @@ export class NavControllerBase extends Ion implements NavController { // create ComponentRef and set it to the entering view enteringView.init(componentFactory.create(childInjector, [])); enteringView._state = ViewState.INITIALIZED; - this._willLoad(enteringView); + this._preLoad(enteringView); } _viewAttachToDOM(view: ViewController, componentRef: ComponentRef, viewport: ViewContainerRef) { assert(view._state === ViewState.INITIALIZED, 'view state must be INITIALIZED'); + // fire willLoad before change detection runs + this._willLoad(view); + // render the component ref instance to the DOM // ******** DOM WRITE **************** viewport.insert(componentRef.hostView, viewport.length); @@ -438,14 +441,11 @@ export class NavControllerBase extends Ion implements NavController { this._renderer.setElementClass(pageElement, view._cssClass, true); } - // TODO: - // componentRef.changeDetectorRef.detectChanges(); + componentRef.changeDetectorRef.detectChanges(); // successfully finished loading the entering view // fire off the "didLoad" lifecycle events this._didLoad(view); - - componentRef.changeDetectorRef.detectChanges(); } _viewTest(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction) { @@ -753,6 +753,12 @@ export class NavControllerBase extends Ion implements NavController { } } + _preLoad(view: ViewController) { + assert(this.isTransitioning(), 'nav controller should be transitioning'); + + view._preLoad(); + } + _willLoad(view: ViewController) { assert(this.isTransitioning(), 'nav controller should be transitioning'); diff --git a/src/navigation/view-controller.ts b/src/navigation/view-controller.ts index 6b8613fefcd..52f3e1a69ec 100644 --- a/src/navigation/view-controller.ts +++ b/src/navigation/view-controller.ts @@ -434,6 +434,15 @@ export class ViewController { /** * @private */ + _preLoad() { + this._lifecycle('PreLoad'); + } + + /** + * @private + * The view has loaded. This event only happens once per view will be created. + * This event is fired before the component and his children have been initialized. + */ _willLoad() { this._lifecycle('WillLoad'); }