mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
fix(nav): do not allow removing all views from a nav
* fix(nav): controller is initialized * fix(nav): transitioning state is a boolean not a timer long async promises in canLeave / canEnter can lead to a false negative of isTransitioning() It is key for the internal consistency of NavController to always know the correct state * fix(nav): returning Promise<false> in canLeave / canEnter works as expected * fix(nav): it is not allowed to pop all the views
This commit is contained in:
@ -258,7 +258,7 @@ export class FirstPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Component({template: ''})
|
@Component({template: '<ion-content></ion-content>'})
|
||||||
export class RedirectPage {
|
export class RedirectPage {
|
||||||
constructor(public navCtrl: NavController) { }
|
constructor(public navCtrl: NavController) { }
|
||||||
ionViewDidEnter() {
|
ionViewDidEnter() {
|
||||||
|
@ -35,7 +35,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
_sbThreshold: number;
|
_sbThreshold: number;
|
||||||
_sbTrns: Transition;
|
_sbTrns: Transition;
|
||||||
_trnsId: number = null;
|
_trnsId: number = null;
|
||||||
_trnsTm: number = 0;
|
_trnsTm: boolean = false;
|
||||||
_viewport: ViewContainerRef;
|
_viewport: ViewContainerRef;
|
||||||
_views: ViewController[] = [];
|
_views: ViewController[] = [];
|
||||||
|
|
||||||
@ -182,6 +182,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
ti.resolve = (hasCompleted: boolean, isAsync: boolean, enteringName: string, leavingName: string, direction: string) => {
|
ti.resolve = (hasCompleted: boolean, isAsync: boolean, enteringName: string, leavingName: string, direction: string) => {
|
||||||
// transition has successfully resolved
|
// transition has successfully resolved
|
||||||
this._trnsId = null;
|
this._trnsId = null;
|
||||||
|
this._init = true;
|
||||||
resolve && resolve(hasCompleted, isAsync, enteringName, leavingName, direction);
|
resolve && resolve(hasCompleted, isAsync, enteringName, leavingName, direction);
|
||||||
|
|
||||||
// let's see if there's another to kick off
|
// let's see if there's another to kick off
|
||||||
@ -252,14 +253,15 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
if (!ti) {
|
if (!ti) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// set that this nav is actively transitioning
|
|
||||||
this.setTransitioning(true);
|
|
||||||
|
|
||||||
// Get entering and leaving views
|
// Get entering and leaving views
|
||||||
const leavingView = this.getActive();
|
const leavingView = this.getActive();
|
||||||
const enteringView = this._getEnteringView(ti, leavingView);
|
const enteringView = this._getEnteringView(ti, leavingView);
|
||||||
|
|
||||||
assert(leavingView || enteringView, 'Both leavingView and enteringView are null');
|
assert(leavingView || enteringView, 'both leavingView and enteringView are null');
|
||||||
|
|
||||||
|
// set that this nav is actively transitioning
|
||||||
|
this.setTransitioning(true);
|
||||||
|
|
||||||
// Initialize enteringView
|
// Initialize enteringView
|
||||||
if (enteringView && isBlank(enteringView._state)) {
|
if (enteringView && isBlank(enteringView._state)) {
|
||||||
@ -332,6 +334,16 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_postViewInit(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction, resolve: TransitionResolveFn) {
|
_postViewInit(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction, resolve: TransitionResolveFn) {
|
||||||
|
assert(leavingView || enteringView, 'Both leavingView and enteringView are null');
|
||||||
|
|
||||||
|
if (!enteringView && !this._isPortal) {
|
||||||
|
console.warn(`You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`,
|
||||||
|
this, this.getNativeElement());
|
||||||
|
|
||||||
|
ti.reject && ti.reject('navigation stack needs at least one root page');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const opts = ti.opts || {};
|
const opts = ti.opts || {};
|
||||||
const insertViews = ti.insertViews;
|
const insertViews = ti.insertViews;
|
||||||
const removeStart = ti.removeStart;
|
const removeStart = ti.removeStart;
|
||||||
@ -494,8 +506,13 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
if (promises.length) {
|
if (promises.length) {
|
||||||
// darn, async promises, gotta wait for them to resolve
|
// darn, async promises, gotta wait for them to resolve
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(() => this._postViewInit(enteringView, leavingView, ti, resolve))
|
.then((values: any[]) => {
|
||||||
.catch(reject);
|
if (values.some(result => result === false)) {
|
||||||
|
reject(`ionViewCanEnter rejected`);
|
||||||
|
} else {
|
||||||
|
this._postViewInit(enteringView, leavingView, ti, resolve);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
} else {
|
} else {
|
||||||
// synchronous and all tests passed! let's move on already
|
// synchronous and all tests passed! let's move on already
|
||||||
this._postViewInit(enteringView, leavingView, ti, resolve);
|
this._postViewInit(enteringView, leavingView, ti, resolve);
|
||||||
@ -606,7 +623,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
const duration = transition.getDuration();
|
const duration = transition.getDuration();
|
||||||
|
|
||||||
// set that this nav is actively transitioning
|
// set that this nav is actively transitioning
|
||||||
this.setTransitioning(true, duration);
|
this.setTransitioning(true);
|
||||||
|
|
||||||
if (transition.isRoot()) {
|
if (transition.isRoot()) {
|
||||||
// this is the top most, or only active transition, so disable the app
|
// this is the top most, or only active transition, so disable the app
|
||||||
@ -892,7 +909,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
if (this._sbTrns && this._sbGesture) {
|
if (this._sbTrns && this._sbGesture) {
|
||||||
// continue to disable the app while actively dragging
|
// continue to disable the app while actively dragging
|
||||||
this._app.setEnabled(false, ACTIVE_TRANSITION_DEFAULT);
|
this._app.setEnabled(false, ACTIVE_TRANSITION_DEFAULT);
|
||||||
this.setTransitioning(true, ACTIVE_TRANSITION_DEFAULT);
|
this.setTransitioning(true);
|
||||||
|
|
||||||
// set the transition animation's progress
|
// set the transition animation's progress
|
||||||
this._sbTrns.progressStep(stepValue);
|
this._sbTrns.progressStep(stepValue);
|
||||||
@ -942,15 +959,11 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isTransitioning(): boolean {
|
isTransitioning(): boolean {
|
||||||
if (this._trnsTm === 0) {
|
return this._trnsTm;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// using a timestamp instead of boolean incase something goes wrong
|
|
||||||
return (this._trnsTm > Date.now());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTransitioning(isTransitioning: boolean, durationPadding: number = ACTIVE_TRANSITION_DEFAULT) {
|
setTransitioning(isTransitioning: boolean) {
|
||||||
this._trnsTm = (isTransitioning ? (Date.now() + durationPadding + ACTIVE_TRANSITION_OFFSET) : 0);
|
this._trnsTm = isTransitioning;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActive(): ViewController {
|
getActive(): ViewController {
|
||||||
|
Reference in New Issue
Block a user