Skip to content

Commit 5567191

Browse files
committed
fix(modal): app.navPop() can dismiss modals
fixes #8692
1 parent 24d45d5 commit 5567191

File tree

4 files changed

+91
-41
lines changed

4 files changed

+91
-41
lines changed

src/components/app/app-root.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Ion } from '../ion';
66
import { OverlayPortal } from '../nav/overlay-portal';
77
import { Platform } from '../../platform/platform';
88
import { nativeTimeout } from '../../util/dom';
9+
import { assert } from '../../util/util';
910

1011
export const AppRootToken = new OpaqueToken('USERROOT');
1112

@@ -103,12 +104,50 @@ export class IonicApp extends Ion implements OnInit {
103104
if (portal === AppPortal.TOAST) {
104105
return this._toastPortal;
105106
}
107+
// Modals need their own overlay becuase we don't want an ActionSheet
108+
// or Alert to trigger lifecycle events inside a modal
106109
if (portal === AppPortal.MODAL) {
107110
return this._modalPortal;
108111
}
109112
return this._overlayPortal;
110113
}
111114

115+
/**
116+
* @private
117+
*/
118+
_getActivePortal(): OverlayPortal {
119+
const defaultPortal = this._overlayPortal;
120+
const modalPortal = this._modalPortal;
121+
122+
assert(defaultPortal, 'default must be valid');
123+
assert(modalPortal, 'modal must be valid');
124+
125+
const hasModal = modalPortal.length() > 0;
126+
const hasDefault = defaultPortal.length() > 0;
127+
128+
if (!hasModal && !hasDefault) {
129+
return null;
130+
131+
} else if (hasModal && hasDefault) {
132+
var defaultIndex = defaultPortal.getActive().getZIndex();
133+
var modalIndex = modalPortal.getActive().getZIndex();
134+
135+
if (defaultIndex > modalIndex) {
136+
return defaultPortal;
137+
} else {
138+
assert(modalIndex > defaultIndex, 'modal and default zIndex can not be equal');
139+
return modalPortal;
140+
}
141+
142+
} if (hasModal) {
143+
return modalPortal;
144+
145+
} else if (hasDefault) {
146+
return defaultPortal;
147+
}
148+
149+
}
150+
112151
/**
113152
* @private
114153
*/

src/components/app/app.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Title } from '@angular/platform-browser';
33

44
import { AppPortal, IonicApp } from './app-root';
55
import { ClickBlock } from '../../util/click-block';
6+
import { runInDev } from '../../util/util';
67
import { Config } from '../../config/config';
78
import { isNav, isTabs, NavOptions, DIRECTION_FORWARD, DIRECTION_BACK } from '../../navigation/nav-util';
89
import { NavController } from '../../navigation/nav-controller';
@@ -72,6 +73,14 @@ export class App {
7273
// register this back button action with a default priority
7374
_platform.registerBackButtonAction(this.navPop.bind(this));
7475
this._disableScrollAssist = _config.getBoolean('disableScrollAssist', false);
76+
77+
runInDev(() => {
78+
// During developement, navPop can be triggered by calling
79+
// window.ClickBackButton();
80+
if (!window['HWBackButton']) {
81+
window['HWBackButton'] = this.navPop.bind(this);
82+
}
83+
});
7584
}
7685

7786
/**
@@ -228,6 +237,10 @@ export class App {
228237
* @private
229238
*/
230239
navPop(): Promise<any> {
240+
if (!this._rootNav || !this.isEnabled()) {
241+
return Promise.resolve();
242+
}
243+
231244
// function used to climb up all parent nav controllers
232245
function navPop(nav: any): Promise<any> {
233246
if (nav) {
@@ -259,34 +272,30 @@ export class App {
259272

260273
// app must be enabled and there must be a
261274
// root nav controller for go back to work
262-
if (this._rootNav && this.isEnabled()) {
263-
const portal = this._appRoot._getPortal();
264-
265-
// first check if the root navigation has any overlays
266-
// opened in it's portal, like alert/actionsheet/popup
267-
if (portal.length() > 0) {
268-
// there is an overlay view in the portal
269-
// let's pop this one off to go back
270-
console.debug('app, goBack pop overlay');
271-
return portal.pop();
272-
}
275+
const portal = this._appRoot._getActivePortal();
276+
277+
// first check if the root navigation has any overlays
278+
// opened in it's portal, like alert/actionsheet/popup
279+
if (portal) {
280+
// there is an overlay view in the portal
281+
// let's pop this one off to go back
282+
console.debug('app, goBack pop overlay');
283+
return portal.pop();
284+
}
273285

274-
// next get the active nav, check itself and climb up all
275-
// of its parent navs until it finds a nav that can pop
276-
let navPromise = navPop(this.getActiveNav());
277-
if (navPromise === null) {
278-
// no views to go back to
279-
// let's exit the app
280-
if (this._config.getBoolean('navExitApp', true)) {
281-
console.debug('app, goBack exitApp');
282-
this._platform.exitApp();
283-
}
286+
// next get the active nav, check itself and climb up all
287+
// of its parent navs until it finds a nav that can pop
288+
let navPromise = navPop(this.getActiveNav());
289+
if (navPromise === null) {
290+
// no views to go back to
291+
// let's exit the app
292+
if (this._config.getBoolean('navExitApp', true)) {
293+
console.debug('app, goBack exitApp');
294+
this._platform.exitApp();
284295
}
285-
286-
return navPromise;
287296
}
288297

289-
return Promise.resolve();
298+
return navPromise;
290299
}
291300

292301
}

src/navigation/view-controller.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,31 @@ export class ViewController {
4444
* Observable to be subscribed to when the current component will become active
4545
* @returns {Observable} Returns an observable
4646
*/
47-
willEnter: EventEmitter<any>;
47+
willEnter: EventEmitter<any> = new EventEmitter();
4848

4949
/**
5050
* Observable to be subscribed to when the current component has become active
5151
* @returns {Observable} Returns an observable
5252
*/
53-
didEnter: EventEmitter<any>;
53+
didEnter: EventEmitter<any> = new EventEmitter();
5454

5555
/**
5656
* Observable to be subscribed to when the current component will no longer be active
5757
* @returns {Observable} Returns an observable
5858
*/
59-
willLeave: EventEmitter<any>;
59+
willLeave: EventEmitter<any> = new EventEmitter();
6060

6161
/**
6262
* Observable to be subscribed to when the current component is no long active
6363
* @returns {Observable} Returns an observable
6464
*/
65-
didLeave: EventEmitter<any>;
65+
didLeave: EventEmitter<any> = new EventEmitter();
6666

6767
/**
6868
* Observable to be subscribed to when the current component has been destroyed
6969
* @returns {Observable} Returns an observable
7070
*/
71-
willUnload: EventEmitter<any>;
71+
willUnload: EventEmitter<any> = new EventEmitter();
7272

7373
/** @private */
7474
data: any;
@@ -105,12 +105,6 @@ export class ViewController {
105105
this.data = (data instanceof NavParams ? data.data : (isPresent(data) ? data : {}));
106106

107107
this._cssClass = rootCssClass;
108-
109-
this.willEnter = new EventEmitter();
110-
this.didEnter = new EventEmitter();
111-
this.willLeave = new EventEmitter();
112-
this.didLeave = new EventEmitter();
113-
this.willUnload = new EventEmitter();
114108
}
115109

116110
/**
@@ -220,13 +214,13 @@ export class ViewController {
220214
*/
221215
enableBack(): boolean {
222216
// update if it's possible to go back from this nav item
223-
if (this._nav) {
224-
let previousItem = this._nav.getPrevious(this);
225-
// the previous view may exist, but if it's about to be destroyed
226-
// it shouldn't be able to go back to
227-
return !!(previousItem);
217+
if (!this._nav) {
218+
return false;
228219
}
229-
return false;
220+
// the previous view may exist, but if it's about to be destroyed
221+
// it shouldn't be able to go back to
222+
const previousItem = this._nav.getPrevious(this);
223+
return !!(previousItem);
230224
}
231225

232226
/**
@@ -278,6 +272,13 @@ export class ViewController {
278272
}
279273
}
280274

275+
/**
276+
* @private
277+
*/
278+
getZIndex(): number {
279+
return this._zIndex;
280+
}
281+
281282
/**
282283
* @private
283284
* DOM WRITE

src/util/mock-providers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const mockIonicApp = function(app: App, config: Config, platform: Platfor
5555
appRoot._loadingPortal = mockOverlayPortal(app, config, platform);
5656
appRoot._toastPortal = mockOverlayPortal(app, config, platform);
5757
appRoot._overlayPortal = mockOverlayPortal(app, config, platform);
58+
appRoot._modalPortal = mockOverlayPortal(app, config, platform);
5859

5960
return appRoot;
6061
};

0 commit comments

Comments
 (0)