mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(navController): update element caching
Also renamed setViews() to setPages(), and moved the index argument in insert() to be the first arg. Closes #641
This commit is contained in:
@@ -77,7 +77,6 @@ import {extend} from '../../util/util';
|
||||
'</div>' +
|
||||
'</action-sheet-wrapper>',
|
||||
host: {
|
||||
'[style.zIndex]': '_zIndex',
|
||||
'role': 'dialog'
|
||||
},
|
||||
directives: [NgFor, NgIf, Icon]
|
||||
|
||||
@@ -125,7 +125,7 @@ export class NavController extends Ion {
|
||||
this._loader = loader;
|
||||
this._viewManager = viewManager;
|
||||
this._zone = zone;
|
||||
this.renderer = renderer;
|
||||
this._renderer = renderer;
|
||||
|
||||
this._views = [];
|
||||
this._trnsTime = 0;
|
||||
@@ -165,8 +165,7 @@ export class NavController extends Ion {
|
||||
*/
|
||||
push(componentType, params = {}, opts = {}, callback) {
|
||||
if (!componentType) {
|
||||
console.debug('invalid componentType to push');
|
||||
return Promise.reject();
|
||||
return Promise.reject('invalid componentType to push');
|
||||
}
|
||||
|
||||
if (typeof componentType !== 'function') {
|
||||
@@ -211,10 +210,6 @@ export class NavController extends Ion {
|
||||
// add the view to the stack
|
||||
this._add(enteringView);
|
||||
|
||||
if (opts.preCleanup !== false) {
|
||||
this._cleanup(enteringView);
|
||||
}
|
||||
|
||||
if (this.router) {
|
||||
// notify router of the state change
|
||||
this.router.stateChange('push', enteringView, params);
|
||||
@@ -292,23 +287,31 @@ export class NavController extends Ion {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let resolve;
|
||||
// ensure the entering view is shown
|
||||
this._renderView(viewCtrl, true);
|
||||
|
||||
let resolve = null;
|
||||
let promise = new Promise(res => { resolve = res; });
|
||||
opts.direction = opts.direction || 'back';
|
||||
|
||||
let leavingView = this.getActive() || new ViewController();
|
||||
|
||||
// get the views to auto remove without having to do a transiton for each
|
||||
// the last view (the currently active one) will do a normal transition out
|
||||
if (this._views.length > 1) {
|
||||
let autoRemoveItems = this._views.slice(targetIndex, this._views.length);
|
||||
let popView;
|
||||
for (let i = 0; i < autoRemoveItems.length; i++) {
|
||||
autoRemoveItems[i].shouldDestroy = true;
|
||||
autoRemoveItems[i].shouldCache = false;
|
||||
autoRemoveItems[i].willUnload();
|
||||
popView = autoRemoveItems[i];
|
||||
popView.shouldDestroy = true;
|
||||
popView.shouldCache = false;
|
||||
popView.willUnload();
|
||||
|
||||
// only the leaving view should be shown, all others hide
|
||||
this._renderView(popView, (popView === leavingView));
|
||||
}
|
||||
}
|
||||
|
||||
let leavingView = this.getPrevious(viewCtrl);
|
||||
|
||||
if (this.router) {
|
||||
this.router.stateChange('pop', viewCtrl);
|
||||
}
|
||||
@@ -328,11 +331,11 @@ export class NavController extends Ion {
|
||||
|
||||
/**
|
||||
* Inserts a view into the nav stack at the specified index.
|
||||
* @param {Component} The name of the component you want to insert into the nav stack
|
||||
* @param {Index} The index where you want to insert the view
|
||||
* @param {Component} The name of the component you want to insert into the nav stack
|
||||
* @returns {Promise} Returns a promise when the view has been inserted into the navigation stack
|
||||
*/
|
||||
insert(componentType, index, params = {}, opts = {}) {
|
||||
insert(index, componentType, params = {}, opts = {}) {
|
||||
if (!componentType || index < 0) {
|
||||
return Promise.reject();
|
||||
}
|
||||
@@ -373,20 +376,29 @@ export class NavController extends Ion {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setViews(components, opts = {}) {
|
||||
console.warn('setViews() deprecated, use setPages() instead');
|
||||
this.setPages(components, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view stack to reflect the given component classes.
|
||||
* @param {TODO} components TODO
|
||||
* @param {TODO} [opts={}] TODO
|
||||
* @returns {Promise} TODO
|
||||
*/
|
||||
setViews(components, opts = {}) {
|
||||
setPages(components, opts = {}) {
|
||||
if (!components || !components.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let leavingView = this.getActive() || new ViewController();
|
||||
|
||||
// if animate has not been set then default to false
|
||||
opts.animate = opts.animate || false;
|
||||
opts.preCleanup = false;
|
||||
|
||||
// ensure leaving views are not cached, and should be destroyed
|
||||
opts.cacheLeavingView = false;
|
||||
@@ -395,10 +407,17 @@ export class NavController extends Ion {
|
||||
// the last view (the currently active one) will do a normal transition out
|
||||
if (this._views.length > 1) {
|
||||
let autoRemoveItems = this._views.slice(0, this._views.length - 1);
|
||||
let popView;
|
||||
for (let i = 0; i < autoRemoveItems.length; i++) {
|
||||
autoRemoveItems[i].shouldDestroy = true;
|
||||
autoRemoveItems[i].shouldCache = false;
|
||||
autoRemoveItems[i].willUnload();
|
||||
popView = autoRemoveItems[i];
|
||||
popView.shouldDestroy = true;
|
||||
popView.shouldCache = false;
|
||||
popView.willUnload();
|
||||
|
||||
if (opts.animate) {
|
||||
// only the leaving view should be shown, all others hide
|
||||
this._renderView(popView, (popView === leavingView));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,15 +425,14 @@ export class NavController extends Ion {
|
||||
let componentType = null;
|
||||
let viewCtrl = null;
|
||||
|
||||
// create the ViewControllers that go before the new active ViewController in the stack
|
||||
// but the previous views won't should render yet
|
||||
// create the ViewControllers that go before the new active ViewController
|
||||
// in the stack, but the previous views shouldn't render yet
|
||||
if (components.length > 1) {
|
||||
let newBeforeItems = components.slice(0, components.length - 1);
|
||||
for (let j = 0; j < newBeforeItems.length; j++) {
|
||||
componentObj = newBeforeItems[j];
|
||||
|
||||
if (componentObj) {
|
||||
|
||||
// could be an object with a componentType property, or it is a componentType
|
||||
componentType = componentObj.componentType || componentObj;
|
||||
|
||||
@@ -446,7 +464,7 @@ export class NavController extends Ion {
|
||||
* @returns {Promise} Returns a promise when done
|
||||
*/
|
||||
setRoot(componentType, params = {}, opts = {}) {
|
||||
return this.setViews([{
|
||||
return this.setPages([{
|
||||
componentType,
|
||||
params
|
||||
}], opts);
|
||||
@@ -462,6 +480,7 @@ export class NavController extends Ion {
|
||||
* @returns {any} TODO
|
||||
*/
|
||||
_transition(enteringView, leavingView, opts, done) {
|
||||
console.debug('_transition', enteringView, leavingView, opts);
|
||||
let self = this;
|
||||
|
||||
if (enteringView === leavingView) {
|
||||
@@ -487,9 +506,8 @@ export class NavController extends Ion {
|
||||
return done(enteringView);
|
||||
}
|
||||
|
||||
self._setZIndex(enteringView.instance, leavingView.instance, opts.direction);
|
||||
|
||||
self._zone.runOutsideAngular(() => {
|
||||
self._setZIndex(enteringView, leavingView, opts.direction);
|
||||
|
||||
enteringView.shouldDestroy = false;
|
||||
enteringView.shouldCache = false;
|
||||
@@ -638,7 +656,7 @@ export class NavController extends Ion {
|
||||
if (this._views.length === 1) {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
rafFrames(38, () => {
|
||||
this.renderer.setElementClass(this.elementRef, 'has-views', true);
|
||||
this._renderer.setElementClass(this.elementRef, 'has-views', true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -647,17 +665,42 @@ export class NavController extends Ion {
|
||||
});
|
||||
}
|
||||
|
||||
_setZIndex(enteringInstance, leavingInstance, direction) {
|
||||
if (!leavingInstance) {
|
||||
enteringInstance._zIndex = 10;
|
||||
_setZIndex(enteringView, leavingView, direction) {
|
||||
let enteringPageRef = enteringView && enteringView.pageRef();
|
||||
if (enteringPageRef) {
|
||||
if (!leavingView || !leavingView.isLoaded()) {
|
||||
enteringView.zIndex = 10;
|
||||
|
||||
} else if (direction === 'back') {
|
||||
// moving back
|
||||
enteringInstance._zIndex = leavingInstance._zIndex - 1;
|
||||
} else if (direction === 'back') {
|
||||
// moving back
|
||||
enteringView.zIndex = leavingView.zIndex - 1;
|
||||
|
||||
} else {
|
||||
// moving forward
|
||||
enteringInstance._zIndex = leavingInstance._zIndex + 1;
|
||||
} else {
|
||||
// moving forward
|
||||
enteringView.zIndex = leavingView.zIndex + 1;
|
||||
}
|
||||
|
||||
if (enteringView.zIndex !== enteringView._zIndex) {
|
||||
this._renderer.setElementStyle(enteringPageRef, 'z-index', enteringView.zIndex);
|
||||
enteringView._zIndex = enteringView.zIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_renderView(viewCtrl, shouldShow) {
|
||||
// using hidden element attribute to display:none and not render views
|
||||
// renderAttr of '' means the hidden attribute will be added
|
||||
// renderAttr of null means the hidden attribute will be removed
|
||||
// doing checks to make sure we only make an update to the element when needed
|
||||
if (shouldShow && viewCtrl._hdnAttr === '' ||
|
||||
!shouldShow && viewCtrl._hdnAttr !== '') {
|
||||
viewCtrl._hdnAttr = (shouldShow ? null : '');
|
||||
this._renderer.setElementAttribute(viewCtrl.pageRef(), 'hidden', viewCtrl._hdnAttr);
|
||||
|
||||
let navbarRef = viewCtrl.navbarRef();
|
||||
if (navbarRef) {
|
||||
this._renderer.setElementAttribute(navbarRef, 'hidden', viewCtrl._hdnAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,9 +942,8 @@ export class NavController extends Ion {
|
||||
destroys.push(view);
|
||||
|
||||
} else {
|
||||
let isActiveView = (view === activeView);
|
||||
let isPreviousView = (view === previousView);
|
||||
view.domCache && view.domCache(isActiveView, isPreviousView);
|
||||
let shouldShow = (view === activeView) || (view === previousView);
|
||||
this._renderView(view, shouldShow);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ class MyCmpTest{}
|
||||
<button ion-item [nav-push]="pushPage" [nav-params]="{id:40}">Push w/ [nav-push] and [nav-params]</button>
|
||||
<button ion-item [nav-push]="[\'FirstPage\', {id: 22}]">Push w/ [nav-push] array and string view name</button>
|
||||
<button ion-item nav-push="FirstPage" [nav-params]="{id: 23}">Push w/ nav-push and [nav-params]</button>
|
||||
<button ion-item (click)="setViews()">setViews() (Go to PrimaryHeaderPage)</button>
|
||||
<button ion-item (click)="setPages()">setPages() (Go to PrimaryHeaderPage)</button>
|
||||
<button ion-item (click)="setRoot()">setRoot(PrimaryHeaderPage) (Go to PrimaryHeaderPage)</button>
|
||||
<button ion-item (click)="nav.pop()">Pop</button>
|
||||
|
||||
@@ -63,12 +63,12 @@ class FirstPage {
|
||||
}
|
||||
}
|
||||
|
||||
setViews() {
|
||||
setPages() {
|
||||
let items = [
|
||||
PrimaryHeaderPage
|
||||
];
|
||||
|
||||
this.nav.setViews(items);
|
||||
this.nav.setPages(items);
|
||||
}
|
||||
|
||||
setRoot(PrimaryHeaderPage) {
|
||||
@@ -99,7 +99,7 @@ class FirstPage {
|
||||
<p><button (click)="pushAnother()">Push to AnotherPage</button></p>
|
||||
<p><button (click)="pushFirstPage()">Push to FirstPage</button></p>
|
||||
<p><button class="e2eFrom2To1" nav-pop>Pop with NavPop (Go back to 1st)</button></p>
|
||||
<p><button (click)="setViews()">setViews() (Go to PrimaryHeaderPage, FirstPage 1st in history)</button></p>
|
||||
<p><button (click)="setPages()">setPages() (Go to PrimaryHeaderPage, FirstPage 1st in history)</button></p>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
@@ -112,13 +112,13 @@ class FullPage {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
setViews() {
|
||||
setPages() {
|
||||
let items = [
|
||||
FirstPage,
|
||||
PrimaryHeaderPage
|
||||
];
|
||||
|
||||
this.nav.setViews(items);
|
||||
this.nav.setPages(items);
|
||||
}
|
||||
|
||||
pushPrimaryHeaderPage() {
|
||||
@@ -168,7 +168,7 @@ class PrimaryHeaderPage {
|
||||
}
|
||||
|
||||
insert() {
|
||||
this.nav.insert(FirstPage, 2);
|
||||
this.nav.insert(2, FirstPage);
|
||||
}
|
||||
|
||||
removeSecond() {
|
||||
|
||||
@@ -39,7 +39,7 @@ class SecondPage {
|
||||
this.nav = nav;
|
||||
}
|
||||
insertPage(){
|
||||
this.nav.insert(InsertPage, 1)
|
||||
this.nav.insert(1, InsertPage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,16 @@ export function run() {
|
||||
}
|
||||
|
||||
function mockCanGoBackFn() {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// beforeEach(inject([Compiler], compiler => {
|
||||
beforeEach(() => {
|
||||
nav = new NavController(null, null, new Config(), null, null, null, null, null);
|
||||
nav._renderer = {
|
||||
setElementAttribute: function(){},
|
||||
setElementStyle: function(){}
|
||||
};
|
||||
});
|
||||
|
||||
it('should exist', () => {
|
||||
@@ -125,12 +129,12 @@ export function run() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("setViews", () => {
|
||||
describe("setPages", () => {
|
||||
it('should return a resolved Promise if components is falsy', done => {
|
||||
let s = jasmine.createSpy('success');
|
||||
let f = jasmine.createSpy('fail');
|
||||
|
||||
let promise = nav.setViews();
|
||||
let promise = nav.setPages();
|
||||
|
||||
promise.then(s, f).then(() => {
|
||||
expect(s).toHaveBeenCalled();
|
||||
@@ -147,7 +151,7 @@ export function run() {
|
||||
let arr = [FirstPage, SecondPage, ThirdPage];
|
||||
|
||||
nav._transition = mockTransitionFn;
|
||||
nav.setViews(arr);
|
||||
nav.setPages(arr);
|
||||
|
||||
//_views[0] will be transitioned out of
|
||||
expect(nav._views[1].componentType).toBe(FirstPage);
|
||||
@@ -161,22 +165,24 @@ export function run() {
|
||||
it('insert page at the specified index', () => {
|
||||
nav._views = [{}, {}, {}];
|
||||
expect(nav._views[2].componentType).toBeUndefined();
|
||||
nav.insert(FirstPage, 2);
|
||||
nav.insert(2, FirstPage);
|
||||
expect(nav._views[2].componentType).toBe(FirstPage);
|
||||
});
|
||||
|
||||
it('push page if index >= _views.length', () => {
|
||||
nav._views = [{}, {}, {}];
|
||||
spyOn(nav, 'push').and.callThrough();
|
||||
nav.insert(FirstPage, 2);
|
||||
nav.insert(2, FirstPage);
|
||||
expect(nav.push).not.toHaveBeenCalled();
|
||||
|
||||
nav._transition = mockTransitionFn;
|
||||
nav.insert(FirstPage, 4);
|
||||
nav.insert(4, FirstPage);
|
||||
expect(nav._views[4].componentType).toBe(FirstPage);
|
||||
expect(nav.push).toHaveBeenCalled();
|
||||
|
||||
nav.insert(FirstPage, 10);
|
||||
nav.setTransitioning(false);
|
||||
|
||||
nav.insert(10, FirstPage);
|
||||
expect(nav._views[5].componentType).toBe(FirstPage);
|
||||
expect(nav.push.calls.count()).toBe(2);
|
||||
});
|
||||
@@ -270,23 +276,30 @@ export function run() {
|
||||
|
||||
describe("_setZIndex", () => {
|
||||
it('should set zIndex 10 on first entering view', () => {
|
||||
let enteringInstance = {};
|
||||
nav._setZIndex(enteringInstance, null, 'forward');
|
||||
expect(enteringInstance._zIndex).toEqual(10);
|
||||
let enteringView = new ViewController();
|
||||
enteringView.setPageRef({});
|
||||
nav._setZIndex(enteringView, null, 'forward');
|
||||
expect(enteringView.zIndex).toEqual(10);
|
||||
});
|
||||
|
||||
it('should set zIndex 1 on second entering view', () => {
|
||||
let leavingInstance = { _zIndex: 0 };
|
||||
let enteringInstance = {};
|
||||
nav._setZIndex(enteringInstance, leavingInstance, 'forward');
|
||||
expect(enteringInstance._zIndex).toEqual(1);
|
||||
let leavingView = new ViewController();
|
||||
leavingView.zIndex = 0;
|
||||
leavingView._loaded = true;
|
||||
let enteringView = new ViewController();
|
||||
enteringView.setPageRef({});
|
||||
nav._setZIndex(enteringView, leavingView, 'forward');
|
||||
expect(enteringView.zIndex).toEqual(1);
|
||||
});
|
||||
|
||||
it('should set zIndex 0 on entering view going back', () => {
|
||||
let leavingInstance = { _zIndex: 1 };
|
||||
let enteringInstance = {};
|
||||
nav._setZIndex(enteringInstance, leavingInstance, 'back');
|
||||
expect(enteringInstance._zIndex).toEqual(0);
|
||||
let leavingView = new ViewController();
|
||||
leavingView.zIndex = 1;
|
||||
leavingView._loaded = true;
|
||||
let enteringView = new ViewController();
|
||||
enteringView.setPageRef({});
|
||||
nav._setZIndex(enteringView, leavingView, 'back');
|
||||
expect(enteringView.zIndex).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -209,8 +209,4 @@ export class ViewController {
|
||||
this.instance.onPageDidUnload && this.instance.onPageDidUnload();
|
||||
}
|
||||
|
||||
domCache(isActiveView, isPreviousView) {
|
||||
this.instance._hidden = (!isActiveView && !isPreviousView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -287,7 +287,6 @@ const OVERLAY_TYPE = 'popup';
|
||||
'</div>' +
|
||||
'</popup-wrapper>',
|
||||
host: {
|
||||
'[style.zIndex]': '_zIndex',
|
||||
'role': 'dialog'
|
||||
},
|
||||
directives: [FORM_DIRECTIVES, NgClass, NgIf, NgFor, Button]
|
||||
|
||||
@@ -70,7 +70,6 @@ export function Page(config={}) {
|
||||
config.host = config.host || {};
|
||||
config.host['[hidden]'] = '_hidden';
|
||||
config.host['[class.tab-subpage]'] = '_tabSubPage';
|
||||
config.host['[style.zIndex]'] = '_zIndex';
|
||||
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||
annotations.push(new Component(config));
|
||||
Reflect.defineMetadata('annotations', annotations, cls);
|
||||
|
||||
Reference in New Issue
Block a user