mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
refactor(nav-controller-base): cleanup some logic
NavControllerBase is the core of ionic 2 navigation. It handles all the transitions and it is complicated code to follow. I am refactoring it to allow future developers and contributors to follow it better. !node.parent now becomes node.isRoot() ViewController does not remove itself from the stack, but two new auxiliar function in nav controller: _insertView() and _removeView() are used to add a view to the stack. And so on... All e2e and unit tests passing...
This commit is contained in:
@ -34,6 +34,7 @@ export class Animation {
|
|||||||
|
|
||||||
parent: Animation;
|
parent: Animation;
|
||||||
opts: AnimationOptions;
|
opts: AnimationOptions;
|
||||||
|
hasChildren: boolean = false;
|
||||||
isPlaying: boolean = false;
|
isPlaying: boolean = false;
|
||||||
hasCompleted: boolean = false;
|
hasCompleted: boolean = false;
|
||||||
|
|
||||||
@ -81,6 +82,7 @@ export class Animation {
|
|||||||
*/
|
*/
|
||||||
add(childAnimation: Animation): Animation {
|
add(childAnimation: Animation): Animation {
|
||||||
childAnimation.parent = this;
|
childAnimation.parent = this;
|
||||||
|
this.hasChildren = true;
|
||||||
this._cL = (this._c = this._c || []).push(childAnimation);
|
this._cL = (this._c = this._c || []).push(childAnimation);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -298,14 +298,14 @@ export class Tab extends NavControllerBase {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_viewInsert(viewCtrl: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
_viewAttachToDOM(viewCtrl: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
||||||
const isTabSubPage = (this.parent._subPages && viewCtrl.index > 0);
|
const isTabSubPage = (this.parent._subPages && viewCtrl.index > 0);
|
||||||
|
|
||||||
if (isTabSubPage) {
|
if (isTabSubPage) {
|
||||||
viewport = this.parent.portal;
|
viewport = this.parent.portal;
|
||||||
}
|
}
|
||||||
|
|
||||||
super._viewInsert(viewCtrl, componentRef, viewport);
|
super._viewAttachToDOM(viewCtrl, componentRef, viewport);
|
||||||
|
|
||||||
if (isTabSubPage) {
|
if (isTabSubPage) {
|
||||||
// add the .tab-subpage css class to tabs pages that should act like subpages
|
// add the .tab-subpage css class to tabs pages that should act like subpages
|
||||||
|
@ -193,9 +193,11 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
while (trns) {
|
while (trns) {
|
||||||
if (trns.enteringView && (trns.enteringView._state !== ViewState.LOADED)) {
|
if (trns.enteringView && (trns.enteringView._state !== ViewState.LOADED)) {
|
||||||
// destroy the entering views and all of their hopes and dreams
|
// destroy the entering views and all of their hopes and dreams
|
||||||
trns.enteringView._destroy(this._renderer);
|
this._destroyView(trns.enteringView);
|
||||||
|
}
|
||||||
|
if (!trns.parent) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!trns.parent) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trns) {
|
if (trns) {
|
||||||
@ -253,6 +255,8 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
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');
|
||||||
|
|
||||||
// Initialize enteringView
|
// Initialize enteringView
|
||||||
if (enteringView && isBlank(enteringView._state)) {
|
if (enteringView && isBlank(enteringView._state)) {
|
||||||
// render the entering view, and all child navs and views
|
// render the entering view, and all child navs and views
|
||||||
@ -327,17 +331,23 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
const opts = ti.opts || {};
|
const opts = ti.opts || {};
|
||||||
const insertViews = ti.insertViews;
|
const insertViews = ti.insertViews;
|
||||||
const removeStart = ti.removeStart;
|
const removeStart = ti.removeStart;
|
||||||
|
let view;
|
||||||
let destroyQueue: ViewController[] = [];
|
let destroyQueue: ViewController[] = [];
|
||||||
|
|
||||||
|
// there are views to remove
|
||||||
if (isPresent(removeStart)) {
|
if (isPresent(removeStart)) {
|
||||||
for (var i = 0; i < ti.removeCount; i++) {
|
for (var i = 0; i < ti.removeCount; i++) {
|
||||||
destroyQueue.push(this._views[i + removeStart]);
|
view = this._views[i + removeStart];
|
||||||
|
assert(view, 'internal error, view in destroyQueue can not be null');
|
||||||
|
if (view && view !== enteringView && view !== leavingView) {
|
||||||
|
destroyQueue.push(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// default the direction to "back"
|
// default the direction to "back"
|
||||||
opts.direction = opts.direction || DIRECTION_BACK;
|
opts.direction = opts.direction || DIRECTION_BACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// there are views to insert
|
||||||
if (insertViews) {
|
if (insertViews) {
|
||||||
// manually set the new view's id if an id was passed in the options
|
// manually set the new view's id if an id was passed in the options
|
||||||
if (isPresent(opts.id)) {
|
if (isPresent(opts.id)) {
|
||||||
@ -346,25 +356,8 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
|
|
||||||
// add the views to the
|
// add the views to the
|
||||||
for (var i = 0; i < insertViews.length; i++) {
|
for (var i = 0; i < insertViews.length; i++) {
|
||||||
var view = insertViews[i];
|
view = insertViews[i];
|
||||||
|
this._insertViewAt(view, ti.insertStart + i);
|
||||||
var existingIndex = this._views.indexOf(view);
|
|
||||||
if (existingIndex > -1) {
|
|
||||||
// this view is already in the stack!!
|
|
||||||
// move it to its new location
|
|
||||||
this._views.splice(ti.insertStart + i, 0, this._views.splice(existingIndex, 1)[0]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// this is a new view to add to the stack
|
|
||||||
// create the new entering view
|
|
||||||
view._setNav(this);
|
|
||||||
|
|
||||||
// give this inserted view an ID
|
|
||||||
view.id = this.id + '-' + (++this._ids);
|
|
||||||
|
|
||||||
// insert the entering view into the correct index in the stack
|
|
||||||
this._views.splice(ti.insertStart + i, 0, view);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ti.enteringRequiresTransition) {
|
if (ti.enteringRequiresTransition) {
|
||||||
@ -372,27 +365,20 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
opts.direction = opts.direction || DIRECTION_FORWARD;
|
opts.direction = opts.direction || DIRECTION_FORWARD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the views to be removed are in the beginning or middle
|
// if the views to be removed are in the beginning or middle
|
||||||
// and there is not a view that needs to visually transition out
|
// and there is not a view that needs to visually transition out
|
||||||
// then just destroy them and don't transition anything
|
// then just destroy them and don't transition anything
|
||||||
for (var i = 0; i < destroyQueue.length; i++) {
|
// batch all of lifecycles together
|
||||||
// batch all of lifecycles together
|
for (view of destroyQueue) {
|
||||||
var view = destroyQueue[i];
|
this._willLeave(view);
|
||||||
if (view && view !== enteringView && view !== leavingView) {
|
this._didLeave(view);
|
||||||
this._willLeave(view);
|
this._willUnload(view);
|
||||||
this._didLeave(view);
|
|
||||||
this._willUnload(view);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (var i = 0; i < destroyQueue.length; i++) {
|
|
||||||
// batch all of the destroys together
|
// once all lifecycle events has been delivered, we can safely detroy the views
|
||||||
var view = destroyQueue[i];
|
for (view of destroyQueue) {
|
||||||
if (view && view !== enteringView && view !== leavingView) {
|
this._destroyView(view);
|
||||||
view._destroy(this._renderer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
destroyQueue.length = 0;
|
|
||||||
|
|
||||||
if (ti.enteringRequiresTransition || ti.leavingRequiresTransition && enteringView !== leavingView) {
|
if (ti.enteringRequiresTransition || ti.leavingRequiresTransition && enteringView !== leavingView) {
|
||||||
// set which animation it should use if it wasn't set yet
|
// set which animation it should use if it wasn't set yet
|
||||||
@ -434,6 +420,27 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
this._willLoad(enteringView);
|
this._willLoad(enteringView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_viewAttachToDOM(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
||||||
|
// successfully finished loading the entering view
|
||||||
|
// fire off the "didLoad" lifecycle events
|
||||||
|
this._didLoad(view);
|
||||||
|
|
||||||
|
// render the component ref instance to the DOM
|
||||||
|
// ******** DOM WRITE ****************
|
||||||
|
viewport.insert(componentRef.hostView, viewport.length);
|
||||||
|
view._state = ViewState.PRE_RENDERED;
|
||||||
|
|
||||||
|
if (view._cssClass) {
|
||||||
|
// the ElementRef of the actual ion-page created
|
||||||
|
var pageElement = componentRef.location.nativeElement;
|
||||||
|
|
||||||
|
// ******** DOM WRITE ****************
|
||||||
|
this._renderer.setElementClass(pageElement, view._cssClass, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentRef.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
_viewTest(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction) {
|
_viewTest(enteringView: ViewController, leavingView: ViewController, ti: TransitionInstruction) {
|
||||||
const promises: Promise<any>[] = [];
|
const promises: Promise<any>[] = [];
|
||||||
const reject = ti.reject;
|
const reject = ti.reject;
|
||||||
@ -442,47 +449,38 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
const leavingTestResult = leavingView._lifecycleTest('Leave');
|
const leavingTestResult = leavingView._lifecycleTest('Leave');
|
||||||
|
|
||||||
if (isPresent(leavingTestResult) && leavingTestResult !== true) {
|
if (leavingTestResult === false) {
|
||||||
if (leavingTestResult instanceof Promise) {
|
// synchronous reject
|
||||||
// async promise
|
reject((leavingTestResult !== false ? leavingTestResult : `ionViewCanLeave rejected`));
|
||||||
promises.push(leavingTestResult);
|
return false;
|
||||||
|
} else if (leavingTestResult instanceof Promise) {
|
||||||
} else {
|
// async promise
|
||||||
// synchronous reject
|
promises.push(leavingTestResult);
|
||||||
reject((leavingTestResult !== false ? leavingTestResult : `ionViewCanLeave rejected`));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enteringView) {
|
if (enteringView) {
|
||||||
const enteringTestResult = enteringView._lifecycleTest('Enter');
|
const enteringTestResult = enteringView._lifecycleTest('Enter');
|
||||||
|
|
||||||
if (isPresent(enteringTestResult) && enteringTestResult !== true) {
|
if (enteringTestResult === false) {
|
||||||
if (enteringTestResult instanceof Promise) {
|
// synchronous reject
|
||||||
// async promise
|
reject((enteringTestResult !== false ? enteringTestResult : `ionViewCanEnter rejected`));
|
||||||
promises.push(enteringTestResult);
|
return false;
|
||||||
|
} else if (enteringTestResult instanceof Promise) {
|
||||||
} else {
|
// async promise
|
||||||
// synchronous reject
|
promises.push(enteringTestResult);
|
||||||
reject((enteringTestResult !== false ? enteringTestResult : `ionViewCanEnter rejected`));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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).then(() => {
|
Promise.all(promises)
|
||||||
// all promises resolved! let's continue
|
.then(() => this._postViewInit(enteringView, leavingView, ti, resolve))
|
||||||
this._postViewInit(enteringView, leavingView, ti, resolve);
|
.catch(reject);
|
||||||
|
} else {
|
||||||
}).catch(reject);
|
// synchronous and all tests passed! let's move on already
|
||||||
return true;
|
this._postViewInit(enteringView, leavingView, ti, resolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
// synchronous and all tests passed! let's move on already
|
|
||||||
this._postViewInit(enteringView, leavingView, ti, resolve);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,27 +506,21 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
|
|
||||||
// create the transition animation from the TransitionController
|
// create the transition animation from the TransitionController
|
||||||
// this will either create the root transition, or add it as a child transition
|
// this will either create the root transition, or add it as a child transition
|
||||||
const trns = this._trnsCtrl.get(this._trnsId, enteringView, leavingView, animationOpts);
|
const transition = this._trnsCtrl.get(this._trnsId, enteringView, leavingView, animationOpts);
|
||||||
|
|
||||||
// ensure any swipeback transitions are cleared out
|
// ensure any swipeback transitions are cleared out
|
||||||
this._sbTrns && this._sbTrns.destroy();
|
this._sbTrns && this._sbTrns.destroy();
|
||||||
|
this._sbTrns = null;
|
||||||
|
|
||||||
if (trns.parent) {
|
// swipe to go back root transition
|
||||||
// this is important for later to know if there
|
if (transition.isRoot() && opts.progressAnimation) {
|
||||||
// are any more child tests to check for
|
this._sbTrns = transition;
|
||||||
trns.parent.hasChildTrns = true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// this is the root transition
|
|
||||||
if (opts.progressAnimation) {
|
|
||||||
this._sbTrns = trns;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trns.registerStart(() => {
|
transition.registerStart(() => {
|
||||||
this._trnsStart(trns, enteringView, leavingView, opts, resolve);
|
this._trnsStart(transition, enteringView, leavingView, opts, resolve);
|
||||||
if (trns.parent) {
|
if (transition.parent) {
|
||||||
trns.parent.start();
|
transition.parent.start();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -537,37 +529,16 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
// this would also render new child navs/views
|
// this would also render new child navs/views
|
||||||
// which may have their very own async canEnter/Leave tests
|
// which may have their very own async canEnter/Leave tests
|
||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
this._viewInsert(enteringView, enteringView._cmp, this._viewport);
|
this._viewAttachToDOM(enteringView, enteringView._cmp, this._viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trns.hasChildTrns) {
|
if (!transition.hasChildren) {
|
||||||
// lowest level transition, so kick it off and let it bubble up to start all of them
|
// lowest level transition, so kick it off and let it bubble up to start all of them
|
||||||
trns.start();
|
transition.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewInsert(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
_trnsStart(transition: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions, resolve: TransitionResolveFn) {
|
||||||
// successfully finished loading the entering view
|
|
||||||
// fire off the "didLoad" lifecycle events
|
|
||||||
this._didLoad(view);
|
|
||||||
|
|
||||||
// render the component ref instance to the DOM
|
|
||||||
// ******** DOM WRITE ****************
|
|
||||||
viewport.insert(componentRef.hostView, viewport.length);
|
|
||||||
view._state = ViewState.PRE_RENDERED;
|
|
||||||
|
|
||||||
if (view._cssClass) {
|
|
||||||
// the ElementRef of the actual ion-page created
|
|
||||||
var pageElement = componentRef.location.nativeElement;
|
|
||||||
|
|
||||||
// ******** DOM WRITE ****************
|
|
||||||
this._renderer.setElementClass(pageElement, view._cssClass, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentRef.changeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
_trnsStart(trns: Transition, enteringView: ViewController, leavingView: ViewController, opts: NavOptions, resolve: TransitionResolveFn) {
|
|
||||||
this._trnsId = null;
|
this._trnsId = null;
|
||||||
|
|
||||||
// set the correct zIndex for the entering and leaving views
|
// set the correct zIndex for the entering and leaving views
|
||||||
@ -578,43 +549,46 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
enteringView && enteringView._domShow(true, this._renderer);
|
enteringView && enteringView._domShow(true, this._renderer);
|
||||||
|
|
||||||
if (leavingView) {
|
// always ensure the leaving view is viewable
|
||||||
// always ensure the leaving view is viewable
|
// ******** DOM WRITE ****************
|
||||||
// ******** DOM WRITE ****************
|
leavingView && leavingView._domShow(true, this._renderer);
|
||||||
leavingView._domShow(true, this._renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the transition
|
// initialize the transition
|
||||||
trns.init();
|
transition.init();
|
||||||
|
|
||||||
if ((!this._init && this._views.length === 1 && !this._isPortal) || this.config.get('animate') === false) {
|
// we should animate (duration > 0) if the pushed page is not the first one (startup)
|
||||||
// the initial load shouldn't animate, unless it's a portal
|
// or if it is a portal (modal, actionsheet, etc.)
|
||||||
|
let isFirstPage = !this._init && this._views.length === 1;
|
||||||
|
let shouldNotAnimate = isFirstPage && !this._isPortal;
|
||||||
|
let canNotAnimate = this.config.get('animate') === false;
|
||||||
|
if (shouldNotAnimate || canNotAnimate) {
|
||||||
opts.animate = false;
|
opts.animate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.animate === false) {
|
if (opts.animate === false) {
|
||||||
// if it was somehow set to not animation, then make the duration zero
|
// if it was somehow set to not animation, then make the duration zero
|
||||||
trns.duration(0);
|
transition.duration(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a callback that needs to run within zone
|
// create a callback that needs to run within zone
|
||||||
// that will fire off the willEnter/Leave lifecycle events at the right time
|
// that will fire off the willEnter/Leave lifecycle events at the right time
|
||||||
trns.beforeAddRead(() => {
|
transition.beforeAddRead(() => {
|
||||||
this._zone.run(this._viewsWillLifecycles.bind(this, enteringView, leavingView));
|
this._zone.run(this._viewsWillLifecycles.bind(this, enteringView, leavingView));
|
||||||
});
|
});
|
||||||
|
|
||||||
// create a callback for when the animation is done
|
// create a callback for when the animation is done
|
||||||
trns.onFinish(() => {
|
transition.onFinish(() => {
|
||||||
// transition animation has ended
|
// transition animation has ended
|
||||||
this._zone.run(this._trnsFinish.bind(this, trns, opts, resolve));
|
this._zone.run(this._trnsFinish.bind(this, transition, opts, resolve));
|
||||||
});
|
});
|
||||||
|
|
||||||
// get the set duration of this transition
|
// get the set duration of this transition
|
||||||
const duration = trns.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, duration);
|
||||||
|
|
||||||
if (!trns.parent) {
|
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
|
||||||
// add XXms to the duration the app is disabled when the keyboard is open
|
// add XXms to the duration the app is disabled when the keyboard is open
|
||||||
|
|
||||||
@ -622,19 +596,21 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
// if this transition has a duration and this is the root transition
|
// if this transition has a duration and this is the root transition
|
||||||
// then set that the app is actively disabled
|
// then set that the app is actively disabled
|
||||||
this._app.setEnabled(false, duration + ACTIVE_TRANSITION_OFFSET);
|
this._app.setEnabled(false, duration + ACTIVE_TRANSITION_OFFSET);
|
||||||
|
} else {
|
||||||
|
console.debug('transition is running but app has not been disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
// cool, let's do this, start the transition
|
// cool, let's do this, start the transition
|
||||||
if (opts.progressAnimation) {
|
if (opts.progressAnimation) {
|
||||||
// this is a swipe to go back, just get the transition progress ready
|
// this is a swipe to go back, just get the transition progress ready
|
||||||
// kick off the swipe animation start
|
// kick off the swipe animation start
|
||||||
trns.progressStart();
|
transition.progressStart();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// only the top level transition should actually start "play"
|
// only the top level transition should actually start "play"
|
||||||
// kick it off and let it play through
|
// kick it off and let it play through
|
||||||
// ******** DOM WRITE ****************
|
// ******** DOM WRITE ****************
|
||||||
trns.play();
|
transition.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -645,32 +621,30 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
leavingView && this._willLeave(leavingView);
|
leavingView && this._willLeave(leavingView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_trnsFinish(trns: Transition, opts: NavOptions, resolve: TransitionResolveFn) {
|
_trnsFinish(transition: Transition, opts: NavOptions, resolve: TransitionResolveFn) {
|
||||||
const hasCompleted = trns.hasCompleted;
|
|
||||||
|
|
||||||
// mainly for testing
|
// mainly for testing
|
||||||
let enteringName: string;
|
let enteringName: string;
|
||||||
let leavingName: string;
|
let leavingName: string;
|
||||||
|
|
||||||
if (hasCompleted) {
|
if (transition.hasCompleted) {
|
||||||
// transition has completed (went from 0 to 1)
|
// transition has completed (went from 0 to 1)
|
||||||
if (trns.enteringView) {
|
if (transition.enteringView) {
|
||||||
enteringName = trns.enteringView.name;
|
enteringName = transition.enteringView.name;
|
||||||
this._didEnter(trns.enteringView);
|
this._didEnter(transition.enteringView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trns.leavingView) {
|
if (transition.leavingView) {
|
||||||
leavingName = trns.leavingView.name;
|
leavingName = transition.leavingView.name;
|
||||||
this._didLeave(trns.leavingView);
|
this._didLeave(transition.leavingView);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._cleanup(trns.enteringView);
|
this._cleanup(transition.enteringView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trns.parent) {
|
if (transition.isRoot()) {
|
||||||
// this is the root transition
|
// this is the root transition
|
||||||
// it's save to destroy this transition
|
// it's save to destroy this transition
|
||||||
this._trnsCtrl.destroy(trns.trnsId);
|
this._trnsCtrl.destroy(transition.trnsId);
|
||||||
|
|
||||||
// it's safe to enable the app again
|
// it's safe to enable the app again
|
||||||
this._app.setEnabled(true);
|
this._app.setEnabled(true);
|
||||||
@ -681,7 +655,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
this._linker.navChange(opts.direction);
|
this._linker.navChange(opts.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.keyboardClose !== false && this._keyboard.isOpen()) {
|
if (opts.keyboardClose !== false) {
|
||||||
// the keyboard is still open!
|
// the keyboard is still open!
|
||||||
// no problem, let's just close for them
|
// no problem, let's just close for them
|
||||||
this._keyboard.close();
|
this._keyboard.close();
|
||||||
@ -689,7 +663,40 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// congrats, we did it!
|
// congrats, we did it!
|
||||||
resolve(hasCompleted, true, enteringName, leavingName, opts.direction);
|
resolve(transition.hasCompleted, true, enteringName, leavingName, opts.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
_insertViewAt(view: ViewController, index: number) {
|
||||||
|
var existingIndex = this._views.indexOf(view);
|
||||||
|
if (existingIndex > -1) {
|
||||||
|
// this view is already in the stack!!
|
||||||
|
// move it to its new location
|
||||||
|
this._views.splice(index, 0, this._views.splice(existingIndex, 1)[0]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// this is a new view to add to the stack
|
||||||
|
// create the new entering view
|
||||||
|
view._setNav(this);
|
||||||
|
|
||||||
|
// give this inserted view an ID
|
||||||
|
view.id = this.id + '-' + (++this._ids);
|
||||||
|
|
||||||
|
// insert the entering view into the correct index in the stack
|
||||||
|
this._views.splice(index, 0, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeView(view: ViewController) {
|
||||||
|
const views = this._views;
|
||||||
|
const index = views.indexOf(view);
|
||||||
|
if (index > -1) {
|
||||||
|
views.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroyView(view: ViewController) {
|
||||||
|
view._destroy(this._renderer);
|
||||||
|
this._removeView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -707,7 +714,7 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
// this view comes after the active view
|
// this view comes after the active view
|
||||||
// let's unload it
|
// let's unload it
|
||||||
this._willUnload(view);
|
this._willUnload(view);
|
||||||
view._destroy(this._renderer);
|
this._destroyView(view);
|
||||||
|
|
||||||
} else if (i < activeViewIndex && !this._isPortal) {
|
} else if (i < activeViewIndex && !this._isPortal) {
|
||||||
// this view comes before the active view
|
// this view comes before the active view
|
||||||
@ -800,9 +807,10 @@ export class NavControllerBase extends Ion implements NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
for (var i = this._views.length - 1; i >= 0; i--) {
|
let view;
|
||||||
this._views[i]._willUnload();
|
for (view of this._views) {
|
||||||
this._views[i]._destroy(this._renderer);
|
view._willUnload();
|
||||||
|
view._destroy(this._renderer);
|
||||||
}
|
}
|
||||||
this._views.length = 0;
|
this._views.length = 0;
|
||||||
|
|
||||||
|
@ -528,26 +528,25 @@ export class ViewController {
|
|||||||
this._cmp.destroy();
|
this._cmp.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._nav) {
|
|
||||||
// remove it from the nav
|
|
||||||
const index = this._nav.indexOf(this);
|
|
||||||
if (index > -1) {
|
|
||||||
this._nav._views.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._nav = this._cmp = this.instance = this._cntDir = this._cntRef = this._hdrDir = this._ftrDir = this._nb = this._onWillDismiss = null;
|
this._nav = this._cmp = this.instance = this._cntDir = this._cntRef = this._hdrDir = this._ftrDir = this._nb = this._onWillDismiss = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_lifecycleTest(lifecycle: string): boolean | string | Promise<any> {
|
_lifecycleTest(lifecycle: string): boolean | Promise<any> {
|
||||||
let instance = this.instance;
|
let instance = this.instance;
|
||||||
let methodName = 'ionViewCan' + lifecycle;
|
let methodName = 'ionViewCan' + lifecycle;
|
||||||
if (instance && instance[methodName]) {
|
if (instance && instance[methodName]) {
|
||||||
try {
|
try {
|
||||||
return instance[methodName]();
|
let result = instance[methodName]();
|
||||||
|
if (result === false) {
|
||||||
|
return false;
|
||||||
|
} else if (result instanceof Promise) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`${this.name} ${methodName} error: ${e.message}`);
|
console.error(`${this.name} ${methodName} error: ${e.message}`);
|
||||||
@ -572,7 +571,6 @@ export class ViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function isViewController(viewCtrl: any) {
|
export function isViewController(viewCtrl: any) {
|
||||||
return !!(viewCtrl && (<ViewController>viewCtrl)._didLoad && (<ViewController>viewCtrl)._willUnload);
|
return !!(viewCtrl && (<ViewController>viewCtrl)._didLoad && (<ViewController>viewCtrl)._willUnload);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export class TransitionController {
|
|||||||
return this._ids++;
|
return this._ids++;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(trnsId: number, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions) {
|
get(trnsId: number, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Transition {
|
||||||
const trns = createTransition(this._config, opts.animation, enteringView, leavingView, opts);
|
const trns = createTransition(this._config, opts.animation, enteringView, leavingView, opts);
|
||||||
trns.trnsId = trnsId;
|
trns.trnsId = trnsId;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Config } from '../config/config';
|
import { Config } from '../config/config';
|
||||||
|
import { Transition } from './transition';
|
||||||
import { IOSTransition } from './transition-ios';
|
import { IOSTransition } from './transition-ios';
|
||||||
import { MDTransition } from './transition-md';
|
import { MDTransition } from './transition-md';
|
||||||
import { WPTransition } from './transition-wp';
|
import { WPTransition } from './transition-wp';
|
||||||
@ -62,7 +63,7 @@ export function registerTransitions(config: Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function createTransition(config: Config, transitionName: string, enteringView: any, leavingView: any, opts: any) {
|
export function createTransition(config: Config, transitionName: string, enteringView: any, leavingView: any, opts: any): Transition {
|
||||||
let TransitionClass: any = config.getTransition(transitionName);
|
let TransitionClass: any = config.getTransition(transitionName);
|
||||||
if (!TransitionClass) {
|
if (!TransitionClass) {
|
||||||
// didn't find a transition animation, default to ios-transition
|
// didn't find a transition animation, default to ios-transition
|
||||||
|
@ -21,10 +21,8 @@ export class Transition extends Animation {
|
|||||||
_trnsStart: Function;
|
_trnsStart: Function;
|
||||||
|
|
||||||
parent: Transition;
|
parent: Transition;
|
||||||
hasChildTrns: boolean = false;
|
|
||||||
trnsId: number;
|
trnsId: number;
|
||||||
|
|
||||||
|
|
||||||
constructor(public enteringView: ViewController, public leavingView: ViewController, opts: AnimationOptions, raf?: Function) {
|
constructor(public enteringView: ViewController, public leavingView: ViewController, opts: AnimationOptions, raf?: Function) {
|
||||||
super(null, opts, raf);
|
super(null, opts, raf);
|
||||||
}
|
}
|
||||||
@ -35,6 +33,10 @@ export class Transition extends Animation {
|
|||||||
this._trnsStart = trnsStart;
|
this._trnsStart = trnsStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isRoot(): boolean {
|
||||||
|
return !this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this._trnsStart && this._trnsStart();
|
this._trnsStart && this._trnsStart();
|
||||||
this._trnsStart = null;
|
this._trnsStart = null;
|
||||||
|
@ -275,9 +275,9 @@ export const mockNavController = function(): NavControllerBase {
|
|||||||
enteringView._state = ViewState.INITIALIZED;
|
enteringView._state = ViewState.INITIALIZED;
|
||||||
};
|
};
|
||||||
|
|
||||||
(<any>nav)._orgViewInsert = nav._viewInsert;
|
(<any>nav)._orgViewInsert = nav._viewAttachToDOM;
|
||||||
|
|
||||||
nav._viewInsert = function(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
nav._viewAttachToDOM = function(view: ViewController, componentRef: ComponentRef<any>, viewport: ViewContainerRef) {
|
||||||
let mockedViewport: any = {
|
let mockedViewport: any = {
|
||||||
insert: () => { }
|
insert: () => { }
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user