fix(modal): app.navPop() can dismiss modals

fixes #8692
This commit is contained in:
Manu Mtz.-Almeida
2016-12-02 11:55:26 +01:00
parent 24d45d5c50
commit 5567191a45
4 changed files with 92 additions and 42 deletions

View File

@ -6,6 +6,7 @@ import { Ion } from '../ion';
import { OverlayPortal } from '../nav/overlay-portal';
import { Platform } from '../../platform/platform';
import { nativeTimeout } from '../../util/dom';
import { assert } from '../../util/util';
export const AppRootToken = new OpaqueToken('USERROOT');
@ -103,12 +104,50 @@ export class IonicApp extends Ion implements OnInit {
if (portal === AppPortal.TOAST) {
return this._toastPortal;
}
// Modals need their own overlay becuase we don't want an ActionSheet
// or Alert to trigger lifecycle events inside a modal
if (portal === AppPortal.MODAL) {
return this._modalPortal;
}
return this._overlayPortal;
}
/**
* @private
*/
_getActivePortal(): OverlayPortal {
const defaultPortal = this._overlayPortal;
const modalPortal = this._modalPortal;
assert(defaultPortal, 'default must be valid');
assert(modalPortal, 'modal must be valid');
const hasModal = modalPortal.length() > 0;
const hasDefault = defaultPortal.length() > 0;
if (!hasModal && !hasDefault) {
return null;
} else if (hasModal && hasDefault) {
var defaultIndex = defaultPortal.getActive().getZIndex();
var modalIndex = modalPortal.getActive().getZIndex();
if (defaultIndex > modalIndex) {
return defaultPortal;
} else {
assert(modalIndex > defaultIndex, 'modal and default zIndex can not be equal');
return modalPortal;
}
} if (hasModal) {
return modalPortal;
} else if (hasDefault) {
return defaultPortal;
}
}
/**
* @private
*/

View File

@ -3,6 +3,7 @@ import { Title } from '@angular/platform-browser';
import { AppPortal, IonicApp } from './app-root';
import { ClickBlock } from '../../util/click-block';
import { runInDev } from '../../util/util';
import { Config } from '../../config/config';
import { isNav, isTabs, NavOptions, DIRECTION_FORWARD, DIRECTION_BACK } from '../../navigation/nav-util';
import { NavController } from '../../navigation/nav-controller';
@ -72,6 +73,14 @@ export class App {
// register this back button action with a default priority
_platform.registerBackButtonAction(this.navPop.bind(this));
this._disableScrollAssist = _config.getBoolean('disableScrollAssist', false);
runInDev(() => {
// During developement, navPop can be triggered by calling
// window.ClickBackButton();
if (!window['HWBackButton']) {
window['HWBackButton'] = this.navPop.bind(this);
}
});
}
/**
@ -228,6 +237,10 @@ export class App {
* @private
*/
navPop(): Promise<any> {
if (!this._rootNav || !this.isEnabled()) {
return Promise.resolve();
}
// function used to climb up all parent nav controllers
function navPop(nav: any): Promise<any> {
if (nav) {
@ -259,12 +272,11 @@ export class App {
// app must be enabled and there must be a
// root nav controller for go back to work
if (this._rootNav && this.isEnabled()) {
const portal = this._appRoot._getPortal();
const portal = this._appRoot._getActivePortal();
// first check if the root navigation has any overlays
// opened in it's portal, like alert/actionsheet/popup
if (portal.length() > 0) {
if (portal) {
// there is an overlay view in the portal
// let's pop this one off to go back
console.debug('app, goBack pop overlay');
@ -286,9 +298,6 @@ export class App {
return navPromise;
}
return Promise.resolve();
}
}
const ACTIVE_SCROLLING_TIME = 100;

View File

@ -44,31 +44,31 @@ export class ViewController {
* Observable to be subscribed to when the current component will become active
* @returns {Observable} Returns an observable
*/
willEnter: EventEmitter<any>;
willEnter: EventEmitter<any> = new EventEmitter();
/**
* Observable to be subscribed to when the current component has become active
* @returns {Observable} Returns an observable
*/
didEnter: EventEmitter<any>;
didEnter: EventEmitter<any> = new EventEmitter();
/**
* Observable to be subscribed to when the current component will no longer be active
* @returns {Observable} Returns an observable
*/
willLeave: EventEmitter<any>;
willLeave: EventEmitter<any> = new EventEmitter();
/**
* Observable to be subscribed to when the current component is no long active
* @returns {Observable} Returns an observable
*/
didLeave: EventEmitter<any>;
didLeave: EventEmitter<any> = new EventEmitter();
/**
* Observable to be subscribed to when the current component has been destroyed
* @returns {Observable} Returns an observable
*/
willUnload: EventEmitter<any>;
willUnload: EventEmitter<any> = new EventEmitter();
/** @private */
data: any;
@ -105,12 +105,6 @@ export class ViewController {
this.data = (data instanceof NavParams ? data.data : (isPresent(data) ? data : {}));
this._cssClass = rootCssClass;
this.willEnter = new EventEmitter();
this.didEnter = new EventEmitter();
this.willLeave = new EventEmitter();
this.didLeave = new EventEmitter();
this.willUnload = new EventEmitter();
}
/**
@ -220,14 +214,14 @@ export class ViewController {
*/
enableBack(): boolean {
// update if it's possible to go back from this nav item
if (this._nav) {
let previousItem = this._nav.getPrevious(this);
if (!this._nav) {
return false;
}
// the previous view may exist, but if it's about to be destroyed
// it shouldn't be able to go back to
const previousItem = this._nav.getPrevious(this);
return !!(previousItem);
}
return false;
}
/**
* @private
@ -278,6 +272,13 @@ export class ViewController {
}
}
/**
* @private
*/
getZIndex(): number {
return this._zIndex;
}
/**
* @private
* DOM WRITE

View File

@ -55,6 +55,7 @@ export const mockIonicApp = function(app: App, config: Config, platform: Platfor
appRoot._loadingPortal = mockOverlayPortal(app, config, platform);
appRoot._toastPortal = mockOverlayPortal(app, config, platform);
appRoot._overlayPortal = mockOverlayPortal(app, config, platform);
appRoot._modalPortal = mockOverlayPortal(app, config, platform);
return appRoot;
};