Skip to content

Commit 6b9e59d

Browse files
committed
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
1 parent af0d84c commit 6b9e59d

File tree

7 files changed

+182
-90
lines changed

7 files changed

+182
-90
lines changed

ionic/components/action-sheet/action-sheet.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,23 @@ import {ViewController} from '../nav/view-controller';
8080
*/
8181
export class ActionSheet extends ViewController {
8282

83-
constructor(opts: {
84-
title?: string,
85-
subTitle?: string,
86-
cssClass?: string,
87-
enableBackdropDismiss?: boolean,
88-
buttons?: Array<any>
89-
} = {}) {
83+
constructor(opts: ActionSheetOptions = {}) {
9084
opts.buttons = opts.buttons || [];
9185
opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
9286

9387
super(ActionSheetCmp, opts);
9488
this.viewType = 'action-sheet';
89+
90+
// by default, actionsheets should not fire lifecycle events of other views
91+
// for example, when an actionsheets enters, the current active view should
92+
// not fire its lifecycle events because it's not conceptually leaving
93+
this.fireOtherLifecycles = false;
9594
}
9695

9796
/**
9897
* @private
9998
*/
100-
getTransitionName(direction) {
99+
getTransitionName(direction: string) {
101100
let key = 'actionSheet' + (direction === 'back' ? 'Leave' : 'Enter');
102101
return this._nav && this._nav.config.get(key);
103102
}
@@ -119,7 +118,7 @@ import {ViewController} from '../nav/view-controller';
119118
/**
120119
* @param {object} button Action sheet button
121120
*/
122-
addButton(button) {
121+
addButton(button: any) {
123122
this.data.buttons.push(button);
124123
}
125124

@@ -148,13 +147,7 @@ import {ViewController} from '../nav/view-controller';
148147
*
149148
* @param {object} opts Action sheet options
150149
*/
151-
static create(opts: {
152-
title?: string,
153-
subTitle?: string,
154-
cssClass?: string,
155-
buttons?: Array<any>,
156-
enableBackdropDismiss?: boolean
157-
} = {}) {
150+
static create(opts: ActionSheetOptions = {}) {
158151
return new ActionSheet(opts);
159152
}
160153

@@ -294,6 +287,13 @@ class ActionSheetCmp {
294287
}
295288
}
296289

290+
export interface ActionSheetOptions {
291+
title?: string;
292+
subTitle?: string;
293+
cssClass?: string;
294+
buttons?: Array<any>;
295+
enableBackdropDismiss?: boolean;
296+
}
297297

298298

299299
class ActionSheetSlideIn extends Transition {

ionic/components/alert/alert.ts

Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -119,35 +119,24 @@ import {ViewController} from '../nav/view-controller';
119119
*/
120120
export class Alert extends ViewController {
121121

122-
constructor(opts: {
123-
title?: string,
124-
subTitle?: string,
125-
message?: string,
126-
cssClass?: string,
127-
inputs?: Array<{
128-
type?: string,
129-
name?: string,
130-
placeholder?: string,
131-
value?: string,
132-
label?: string,
133-
checked?: boolean,
134-
id?: string
135-
}>,
136-
buttons?: Array<any>,
137-
enableBackdropDismiss?: boolean
138-
} = {}) {
122+
constructor(opts: AlertOptions = {}) {
139123
opts.inputs = opts.inputs || [];
140124
opts.buttons = opts.buttons || [];
141125
opts.enableBackdropDismiss = isDefined(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
142126

143127
super(AlertCmp, opts);
144128
this.viewType = 'alert';
129+
130+
// by default, alerts should not fire lifecycle events of other views
131+
// for example, when an alert enters, the current active view should
132+
// not fire its lifecycle events because it's not conceptually leaving
133+
this.fireOtherLifecycles = false;
145134
}
146135

147136
/**
148137
* @private
149138
*/
150-
getTransitionName(direction) {
139+
getTransitionName(direction: string) {
151140
let key = (direction === 'back' ? 'alertLeave' : 'alertEnter');
152141
return this._nav && this._nav.config.get(key);
153142
}
@@ -185,15 +174,7 @@ export class Alert extends ViewController {
185174
/**
186175
* @param {object} input Alert input
187176
*/
188-
addInput(input: {
189-
type?: string,
190-
name?: string,
191-
placeholder?: string,
192-
value?: string,
193-
label?: string,
194-
checked?: boolean,
195-
id?: string
196-
}) {
177+
addInput(input: AlertInputOptions) {
197178
this.data.inputs.push(input);
198179
}
199180

@@ -214,23 +195,7 @@ export class Alert extends ViewController {
214195
/**
215196
* @param {object} opts Alert options
216197
*/
217-
static create(opts: {
218-
title?: string,
219-
subTitle?: string,
220-
message?: string,
221-
cssClass?: string,
222-
inputs?: Array<{
223-
type?: string,
224-
name?: string,
225-
placeholder?: string,
226-
value?: string,
227-
label?: string,
228-
checked?: boolean,
229-
id?: string
230-
}>,
231-
buttons?: Array<any>,
232-
enableBackdropDismiss?: boolean
233-
} = {}) {
198+
static create(opts: AlertOptions = {}) {
234199
return new Alert(opts);
235200
}
236201

@@ -485,6 +450,26 @@ class AlertCmp {
485450
}
486451
}
487452

453+
export interface AlertOptions {
454+
title?: string;
455+
subTitle?: string;
456+
message?: string;
457+
cssClass?: string;
458+
inputs?: Array<AlertInputOptions>;
459+
buttons?: Array<any>;
460+
enableBackdropDismiss?: boolean;
461+
}
462+
463+
export interface AlertInputOptions {
464+
type?: string;
465+
name?: string;
466+
placeholder?: string;
467+
value?: string;
468+
label?: string;
469+
checked?: boolean;
470+
id?: string;
471+
}
472+
488473

489474
/**
490475
* Animations for alerts

ionic/components/alert/test/basic/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,14 @@ class E2EPage {
260260

261261
this.nav.present(alert);
262262
}
263+
264+
onPageDidLeave() {
265+
console.log('E2EPage, onPageDidLeave');
266+
}
267+
268+
onPageDidEnter() {
269+
console.log('E2EPage, onPageDidEnter');
270+
}
263271
}
264272

265273

ionic/components/nav/nav-controller.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -978,8 +978,17 @@ export class NavController extends Ion {
978978
}
979979

980980
// call each view's lifecycle events
981-
enteringView.willEnter();
982-
leavingView.willLeave();
981+
if (leavingView.fireOtherLifecycles) {
982+
// only fire entering lifecycle if the leaving
983+
// view hasn't explicitly set not to
984+
enteringView.willEnter();
985+
}
986+
987+
if (enteringView.fireOtherLifecycles) {
988+
// only fire leaving lifecycle if the entering
989+
// view hasn't explicitly set not to
990+
leavingView.willLeave();
991+
}
983992

984993
} else {
985994
// this view is being preloaded, don't call lifecycle events
@@ -1080,8 +1089,17 @@ export class NavController extends Ion {
10801089
this._zone.run(() => {
10811090

10821091
if (!opts.preload && hasCompleted) {
1083-
enteringView.didEnter();
1084-
leavingView.didLeave();
1092+
if (leavingView.fireOtherLifecycles) {
1093+
// only fire entering lifecycle if the leaving
1094+
// view hasn't explicitly set not to
1095+
enteringView.didEnter();
1096+
}
1097+
1098+
if (enteringView.fireOtherLifecycles) {
1099+
// only fire leaving lifecycle if the entering
1100+
// view hasn't explicitly set not to
1101+
leavingView.didLeave();
1102+
}
10851103
}
10861104

10871105
if (enteringView.state === STATE_INACTIVE) {

ionic/components/nav/test/nav-controller.spec.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,42 @@ export function run() {
393393
expect(leavingView.willLeave).toHaveBeenCalled();
394394
});
395395

396+
it('should not call willEnter when the leaving view has fireOtherLifecycles not true', () => {
397+
let enteringView = new ViewController(Page1);
398+
let leavingView = new ViewController(Page2);
399+
var navOptions: NavOptions = {};
400+
var done = () => {};
401+
nav._beforeTrans = () => {}; //prevent running beforeTrans for tests
402+
403+
spyOn(enteringView, 'willEnter');
404+
spyOn(leavingView, 'willLeave');
405+
406+
leavingView.fireOtherLifecycles = false;
407+
408+
nav._postRender(1, enteringView, leavingView, false, navOptions, done);
409+
410+
expect(enteringView.willEnter).not.toHaveBeenCalled();
411+
expect(leavingView.willLeave).toHaveBeenCalled();
412+
});
413+
414+
it('should not call willLeave when the entering view has fireOtherLifecycles not true', () => {
415+
let enteringView = new ViewController(Page1);
416+
let leavingView = new ViewController(Page2);
417+
var navOptions: NavOptions = {};
418+
var done = () => {};
419+
nav._beforeTrans = () => {}; //prevent running beforeTrans for tests
420+
421+
spyOn(enteringView, 'willEnter');
422+
spyOn(leavingView, 'willLeave');
423+
424+
enteringView.fireOtherLifecycles = false;
425+
426+
nav._postRender(1, enteringView, leavingView, false, navOptions, done);
427+
428+
expect(enteringView.willEnter).toHaveBeenCalled();
429+
expect(leavingView.willLeave).not.toHaveBeenCalled();
430+
});
431+
396432
it('should not call willLeave on leaving view when it is being preloaded', () => {
397433
let enteringView = new ViewController(Page1);
398434
let leavingView = new ViewController(Page2);
@@ -495,6 +531,46 @@ export function run() {
495531
expect(doneCalled).toBe(true);
496532
});
497533

534+
it('should not call didLeave when enteringView set fireOtherLifecycles to false', () => {
535+
let enteringView = new ViewController();
536+
let leavingView = new ViewController();
537+
let navOpts: NavOptions = {};
538+
let hasCompleted = true;
539+
let doneCalled = false;
540+
let done = () => {doneCalled = true;}
541+
542+
enteringView.fireOtherLifecycles = false;
543+
544+
spyOn(enteringView, 'didEnter');
545+
spyOn(leavingView, 'didLeave');
546+
547+
nav._afterTrans(enteringView, leavingView, navOpts, hasCompleted, done);
548+
549+
expect(enteringView.didEnter).toHaveBeenCalled();
550+
expect(leavingView.didLeave).not.toHaveBeenCalled();
551+
expect(doneCalled).toBe(true);
552+
});
553+
554+
it('should not call didEnter when leavingView set fireOtherLifecycles to false', () => {
555+
let enteringView = new ViewController();
556+
let leavingView = new ViewController();
557+
let navOpts: NavOptions = {};
558+
let hasCompleted = true;
559+
let doneCalled = false;
560+
let done = () => {doneCalled = true;}
561+
562+
leavingView.fireOtherLifecycles = false;
563+
564+
spyOn(enteringView, 'didEnter');
565+
spyOn(leavingView, 'didLeave');
566+
567+
nav._afterTrans(enteringView, leavingView, navOpts, hasCompleted, done);
568+
569+
expect(enteringView.didEnter).not.toHaveBeenCalled();
570+
expect(leavingView.didLeave).toHaveBeenCalled();
571+
expect(doneCalled).toBe(true);
572+
});
573+
498574
it('should not call didEnter/didLeave when not hasCompleted', () => {
499575
let enteringView = new ViewController();
500576
let leavingView = new ViewController();

0 commit comments

Comments
 (0)