mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(NavController): prevent other lifecycle events from firing
For Alert and ActionSheet, the currently active page’s leaving lifecycle event should not fire. However, for Modal, the currently active page’s leaving lifecycle should fire. Closes #5516
This commit is contained in:
@@ -80,24 +80,23 @@ import {ViewController} from '../nav/view-controller';
|
||||
*/
|
||||
export class ActionSheet extends ViewController {
|
||||
|
||||
constructor(opts: {
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
cssClass?: string,
|
||||
enableBackdropDismiss?: boolean,
|
||||
buttons?: Array<any>
|
||||
} = {}) {
|
||||
constructor(opts: ActionSheetOptions = {}) {
|
||||
opts.buttons = opts.buttons || [];
|
||||
opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
|
||||
|
||||
super(ActionSheetCmp, opts);
|
||||
this.viewType = 'action-sheet';
|
||||
|
||||
// by default, actionsheets should not fire lifecycle events of other views
|
||||
// for example, when an actionsheets enters, the current active view should
|
||||
// not fire its lifecycle events because it's not conceptually leaving
|
||||
this.fireOtherLifecycles = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransitionName(direction) {
|
||||
getTransitionName(direction: string) {
|
||||
let key = 'actionSheet' + (direction === 'back' ? 'Leave' : 'Enter');
|
||||
return this._nav && this._nav.config.get(key);
|
||||
}
|
||||
@@ -119,7 +118,7 @@ import {ViewController} from '../nav/view-controller';
|
||||
/**
|
||||
* @param {object} button Action sheet button
|
||||
*/
|
||||
addButton(button) {
|
||||
addButton(button: any) {
|
||||
this.data.buttons.push(button);
|
||||
}
|
||||
|
||||
@@ -148,13 +147,7 @@ import {ViewController} from '../nav/view-controller';
|
||||
*
|
||||
* @param {object} opts Action sheet options
|
||||
*/
|
||||
static create(opts: {
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
cssClass?: string,
|
||||
buttons?: Array<any>,
|
||||
enableBackdropDismiss?: boolean
|
||||
} = {}) {
|
||||
static create(opts: ActionSheetOptions = {}) {
|
||||
return new ActionSheet(opts);
|
||||
}
|
||||
|
||||
@@ -294,6 +287,13 @@ class ActionSheetCmp {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ActionSheetOptions {
|
||||
title?: string;
|
||||
subTitle?: string;
|
||||
cssClass?: string;
|
||||
buttons?: Array<any>;
|
||||
enableBackdropDismiss?: boolean;
|
||||
}
|
||||
|
||||
|
||||
class ActionSheetSlideIn extends Transition {
|
||||
|
||||
@@ -119,35 +119,24 @@ import {ViewController} from '../nav/view-controller';
|
||||
*/
|
||||
export class Alert extends ViewController {
|
||||
|
||||
constructor(opts: {
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
message?: string,
|
||||
cssClass?: string,
|
||||
inputs?: Array<{
|
||||
type?: string,
|
||||
name?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
label?: string,
|
||||
checked?: boolean,
|
||||
id?: string
|
||||
}>,
|
||||
buttons?: Array<any>,
|
||||
enableBackdropDismiss?: boolean
|
||||
} = {}) {
|
||||
constructor(opts: AlertOptions = {}) {
|
||||
opts.inputs = opts.inputs || [];
|
||||
opts.buttons = opts.buttons || [];
|
||||
opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
|
||||
|
||||
super(AlertCmp, opts);
|
||||
this.viewType = 'alert';
|
||||
|
||||
// by default, alerts should not fire lifecycle events of other views
|
||||
// for example, when an alert enters, the current active view should
|
||||
// not fire its lifecycle events because it's not conceptually leaving
|
||||
this.fireOtherLifecycles = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getTransitionName(direction) {
|
||||
getTransitionName(direction: string) {
|
||||
let key = (direction === 'back' ? 'alertLeave' : 'alertEnter');
|
||||
return this._nav && this._nav.config.get(key);
|
||||
}
|
||||
@@ -185,15 +174,7 @@ export class Alert extends ViewController {
|
||||
/**
|
||||
* @param {object} input Alert input
|
||||
*/
|
||||
addInput(input: {
|
||||
type?: string,
|
||||
name?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
label?: string,
|
||||
checked?: boolean,
|
||||
id?: string
|
||||
}) {
|
||||
addInput(input: AlertInputOptions) {
|
||||
this.data.inputs.push(input);
|
||||
}
|
||||
|
||||
@@ -214,23 +195,7 @@ export class Alert extends ViewController {
|
||||
/**
|
||||
* @param {object} opts Alert options
|
||||
*/
|
||||
static create(opts: {
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
message?: string,
|
||||
cssClass?: string,
|
||||
inputs?: Array<{
|
||||
type?: string,
|
||||
name?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
label?: string,
|
||||
checked?: boolean,
|
||||
id?: string
|
||||
}>,
|
||||
buttons?: Array<any>,
|
||||
enableBackdropDismiss?: boolean
|
||||
} = {}) {
|
||||
static create(opts: AlertOptions = {}) {
|
||||
return new Alert(opts);
|
||||
}
|
||||
|
||||
@@ -485,6 +450,26 @@ class AlertCmp {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AlertOptions {
|
||||
title?: string;
|
||||
subTitle?: string;
|
||||
message?: string;
|
||||
cssClass?: string;
|
||||
inputs?: Array<AlertInputOptions>;
|
||||
buttons?: Array<any>;
|
||||
enableBackdropDismiss?: boolean;
|
||||
}
|
||||
|
||||
export interface AlertInputOptions {
|
||||
type?: string;
|
||||
name?: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
label?: string;
|
||||
checked?: boolean;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animations for alerts
|
||||
|
||||
@@ -260,6 +260,14 @@ class E2EPage {
|
||||
|
||||
this.nav.present(alert);
|
||||
}
|
||||
|
||||
onPageDidLeave() {
|
||||
console.log('E2EPage, onPageDidLeave');
|
||||
}
|
||||
|
||||
onPageDidEnter() {
|
||||
console.log('E2EPage, onPageDidEnter');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -978,8 +978,17 @@ export class NavController extends Ion {
|
||||
}
|
||||
|
||||
// call each view's lifecycle events
|
||||
enteringView.willEnter();
|
||||
leavingView.willLeave();
|
||||
if (leavingView.fireOtherLifecycles) {
|
||||
// only fire entering lifecycle if the leaving
|
||||
// view hasn't explicitly set not to
|
||||
enteringView.willEnter();
|
||||
}
|
||||
|
||||
if (enteringView.fireOtherLifecycles) {
|
||||
// only fire leaving lifecycle if the entering
|
||||
// view hasn't explicitly set not to
|
||||
leavingView.willLeave();
|
||||
}
|
||||
|
||||
} else {
|
||||
// this view is being preloaded, don't call lifecycle events
|
||||
@@ -1080,8 +1089,17 @@ export class NavController extends Ion {
|
||||
this._zone.run(() => {
|
||||
|
||||
if (!opts.preload && hasCompleted) {
|
||||
enteringView.didEnter();
|
||||
leavingView.didLeave();
|
||||
if (leavingView.fireOtherLifecycles) {
|
||||
// only fire entering lifecycle if the leaving
|
||||
// view hasn't explicitly set not to
|
||||
enteringView.didEnter();
|
||||
}
|
||||
|
||||
if (enteringView.fireOtherLifecycles) {
|
||||
// only fire leaving lifecycle if the entering
|
||||
// view hasn't explicitly set not to
|
||||
leavingView.didLeave();
|
||||
}
|
||||
}
|
||||
|
||||
if (enteringView.state === STATE_INACTIVE) {
|
||||
|
||||
@@ -393,6 +393,42 @@ export function run() {
|
||||
expect(leavingView.willLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call willEnter when the leaving view has fireOtherLifecycles not true', () => {
|
||||
let enteringView = new ViewController(Page1);
|
||||
let leavingView = new ViewController(Page2);
|
||||
var navOptions: NavOptions = {};
|
||||
var done = () => {};
|
||||
nav._beforeTrans = () => {}; //prevent running beforeTrans for tests
|
||||
|
||||
spyOn(enteringView, 'willEnter');
|
||||
spyOn(leavingView, 'willLeave');
|
||||
|
||||
leavingView.fireOtherLifecycles = false;
|
||||
|
||||
nav._postRender(1, enteringView, leavingView, false, navOptions, done);
|
||||
|
||||
expect(enteringView.willEnter).not.toHaveBeenCalled();
|
||||
expect(leavingView.willLeave).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call willLeave when the entering view has fireOtherLifecycles not true', () => {
|
||||
let enteringView = new ViewController(Page1);
|
||||
let leavingView = new ViewController(Page2);
|
||||
var navOptions: NavOptions = {};
|
||||
var done = () => {};
|
||||
nav._beforeTrans = () => {}; //prevent running beforeTrans for tests
|
||||
|
||||
spyOn(enteringView, 'willEnter');
|
||||
spyOn(leavingView, 'willLeave');
|
||||
|
||||
enteringView.fireOtherLifecycles = false;
|
||||
|
||||
nav._postRender(1, enteringView, leavingView, false, navOptions, done);
|
||||
|
||||
expect(enteringView.willEnter).toHaveBeenCalled();
|
||||
expect(leavingView.willLeave).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call willLeave on leaving view when it is being preloaded', () => {
|
||||
let enteringView = new ViewController(Page1);
|
||||
let leavingView = new ViewController(Page2);
|
||||
@@ -495,6 +531,46 @@ export function run() {
|
||||
expect(doneCalled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not call didLeave when enteringView set fireOtherLifecycles to false', () => {
|
||||
let enteringView = new ViewController();
|
||||
let leavingView = new ViewController();
|
||||
let navOpts: NavOptions = {};
|
||||
let hasCompleted = true;
|
||||
let doneCalled = false;
|
||||
let done = () => {doneCalled = true;}
|
||||
|
||||
enteringView.fireOtherLifecycles = false;
|
||||
|
||||
spyOn(enteringView, 'didEnter');
|
||||
spyOn(leavingView, 'didLeave');
|
||||
|
||||
nav._afterTrans(enteringView, leavingView, navOpts, hasCompleted, done);
|
||||
|
||||
expect(enteringView.didEnter).toHaveBeenCalled();
|
||||
expect(leavingView.didLeave).not.toHaveBeenCalled();
|
||||
expect(doneCalled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not call didEnter when leavingView set fireOtherLifecycles to false', () => {
|
||||
let enteringView = new ViewController();
|
||||
let leavingView = new ViewController();
|
||||
let navOpts: NavOptions = {};
|
||||
let hasCompleted = true;
|
||||
let doneCalled = false;
|
||||
let done = () => {doneCalled = true;}
|
||||
|
||||
leavingView.fireOtherLifecycles = false;
|
||||
|
||||
spyOn(enteringView, 'didEnter');
|
||||
spyOn(leavingView, 'didLeave');
|
||||
|
||||
nav._afterTrans(enteringView, leavingView, navOpts, hasCompleted, done);
|
||||
|
||||
expect(enteringView.didEnter).not.toHaveBeenCalled();
|
||||
expect(leavingView.didLeave).toHaveBeenCalled();
|
||||
expect(doneCalled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not call didEnter/didLeave when not hasCompleted', () => {
|
||||
let enteringView = new ViewController();
|
||||
let leavingView = new ViewController();
|
||||
|
||||
@@ -64,6 +64,13 @@ export class ViewController {
|
||||
*/
|
||||
onReady: Function;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* If this is currently the active view, then set to false
|
||||
* if it does not want the other views to fire their own lifecycles.
|
||||
*/
|
||||
fireOtherLifecycles: boolean = true;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -86,7 +93,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
emit(data: any) {
|
||||
emit(data?: any) {
|
||||
this._emitter.emit(data);
|
||||
}
|
||||
|
||||
@@ -94,7 +101,7 @@ export class ViewController {
|
||||
this._onDismiss = callback;
|
||||
}
|
||||
|
||||
dismiss(data: any, role?: any) {
|
||||
dismiss(data?: any, role?: any) {
|
||||
this._onDismiss && this._onDismiss(data, role);
|
||||
return this._nav.remove(this._nav.indexOf(this), 1, this._leavingOpts);
|
||||
}
|
||||
@@ -319,14 +326,14 @@ export class ViewController {
|
||||
*
|
||||
* @returns {boolean} Returns a boolean if this Page has a navbar or not.
|
||||
*/
|
||||
hasNavbar() {
|
||||
hasNavbar(): boolean {
|
||||
return !!this.getNavbar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarRef() {
|
||||
navbarRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getElementRef();
|
||||
}
|
||||
@@ -334,7 +341,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
titleRef() {
|
||||
titleRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getTitleRef();
|
||||
}
|
||||
@@ -342,7 +349,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarItemRefs() {
|
||||
navbarItemRefs(): Array<ElementRef> {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getItemRefs();
|
||||
}
|
||||
@@ -350,7 +357,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
backBtnRef() {
|
||||
backBtnRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackButtonRef();
|
||||
}
|
||||
@@ -358,7 +365,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
backBtnTextRef() {
|
||||
backBtnTextRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackButtonTextRef();
|
||||
}
|
||||
@@ -366,7 +373,7 @@ export class ViewController {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
navbarBgRef() {
|
||||
navbarBgRef(): ElementRef {
|
||||
let navbar = this.getNavbar();
|
||||
return navbar && navbar.getBackgroundRef();
|
||||
}
|
||||
@@ -388,7 +395,7 @@ export class ViewController {
|
||||
*
|
||||
* @param {string} backButtonText Set the back button text.
|
||||
*/
|
||||
setBackButtonText(val) {
|
||||
setBackButtonText(val: string) {
|
||||
let navbar = this.getNavbar();
|
||||
if (navbar) {
|
||||
navbar.setBackButtonText(val);
|
||||
@@ -399,7 +406,7 @@ export class ViewController {
|
||||
* Set if the back button for the current view is visible or not. Be sure to wrap this in `onPageWillEnter` to make sure the has been compleltly rendered.
|
||||
* @param {boolean} Set if this Page's back button should show or not.
|
||||
*/
|
||||
showBackButton(shouldShow) {
|
||||
showBackButton(shouldShow: boolean) {
|
||||
let navbar = this.getNavbar();
|
||||
if (navbar) {
|
||||
navbar.hideBackButton = !shouldShow;
|
||||
@@ -491,12 +498,12 @@ export class ViewController {
|
||||
|
||||
}
|
||||
|
||||
function ctrlFn(viewCtrl, fnName) {
|
||||
function ctrlFn(viewCtrl: ViewController, fnName: string) {
|
||||
if (viewCtrl.instance && viewCtrl.instance[fnName]) {
|
||||
try {
|
||||
viewCtrl.instance[fnName]();
|
||||
} catch(e) {
|
||||
console.error(fnName + ': ' + e.message);
|
||||
console.error(viewCtrl.name + ' ' + fnName + ': ' + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {Icon} from '../icon/icon';
|
||||
import {ToolbarBase} from '../toolbar/toolbar';
|
||||
import {Config} from '../../config/config';
|
||||
import {IonicApp} from '../app/app';
|
||||
import {isTrueProperty} from '../../util/util';
|
||||
import {ViewController} from '../nav/view-controller';
|
||||
import {NavController} from '../nav/nav-controller';
|
||||
|
||||
@@ -93,7 +94,7 @@ class ToolbarBackground {
|
||||
selector: 'ion-navbar',
|
||||
template:
|
||||
'<div class="toolbar-background"></div>' +
|
||||
'<button class="back-button bar-button bar-button-default" [hidden]="hideBackButton">' +
|
||||
'<button class="back-button bar-button bar-button-default" [hidden]="_hideBb">' +
|
||||
'<span class="button-inner">' +
|
||||
'<ion-icon class="back-button-icon" [name]="_bbIcon"></ion-icon>' +
|
||||
'<span class="back-button-text">' +
|
||||
@@ -117,7 +118,8 @@ class ToolbarBackground {
|
||||
export class Navbar extends ToolbarBase {
|
||||
private _bbIcon: string;
|
||||
private _bbText: string;
|
||||
private _hidden: boolean;
|
||||
private _hidden: boolean = false;
|
||||
private _hideBb: boolean = false;
|
||||
private _bbRef: ElementRef;
|
||||
private _bbtRef: ElementRef;
|
||||
private _bgRef: ElementRef;
|
||||
@@ -125,7 +127,13 @@ export class Navbar extends ToolbarBase {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Input() hideBackButton: any;
|
||||
@Input()
|
||||
get hideBackButton(): boolean {
|
||||
return this._hideBb;
|
||||
}
|
||||
set hideBackButton(val: boolean) {
|
||||
this._hideBb = isTrueProperty(val);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _app: IonicApp,
|
||||
@@ -142,16 +150,6 @@ export class Navbar extends ToolbarBase {
|
||||
this._bbText = config.get('backButtonText');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnInit() {
|
||||
let hideBackButton = this.hideBackButton;
|
||||
if (typeof hideBackButton === 'string') {
|
||||
this.hideBackButton = (hideBackButton === '' || hideBackButton === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user