mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
@ -7,7 +7,7 @@ import {IonicApp} from '../app/app';
|
|||||||
import {Keyboard} from '../../util/keyboard';
|
import {Keyboard} from '../../util/keyboard';
|
||||||
import {NavParams} from './nav-params';
|
import {NavParams} from './nav-params';
|
||||||
import {NavRouter} from './nav-router';
|
import {NavRouter} from './nav-router';
|
||||||
import {pascalCaseToDashCase, isTrueProperty} from '../../util/util';
|
import {pascalCaseToDashCase, isTrueProperty, isUndefined} from '../../util/util';
|
||||||
import {raf} from '../../util/dom';
|
import {raf} from '../../util/dom';
|
||||||
import {SwipeBackGesture} from './swipe-back';
|
import {SwipeBackGesture} from './swipe-back';
|
||||||
import {Transition} from '../../transitions/transition';
|
import {Transition} from '../../transitions/transition';
|
||||||
@ -119,7 +119,7 @@ export class NavController extends Ion {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
id: number;
|
id: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -163,7 +163,7 @@ export class NavController extends Ion {
|
|||||||
this._sbEnabled = config.getBoolean('swipeBackEnabled') || false;
|
this._sbEnabled = config.getBoolean('swipeBackEnabled') || false;
|
||||||
this._sbThreshold = config.get('swipeBackThreshold') || 40;
|
this._sbThreshold = config.get('swipeBackThreshold') || 40;
|
||||||
|
|
||||||
this.id = ++ctrlIds;
|
this.id = (++ctrlIds).toString();
|
||||||
|
|
||||||
// build a new injector for child ViewControllers to use
|
// build a new injector for child ViewControllers to use
|
||||||
this.providers = Injector.resolve([
|
this.providers = Injector.resolve([
|
||||||
@ -622,6 +622,12 @@ export class NavController extends Ion {
|
|||||||
let activeView = this.getByState(STATE_TRANS_ENTER) ||
|
let activeView = this.getByState(STATE_TRANS_ENTER) ||
|
||||||
this.getByState(STATE_INIT_ENTER) ||
|
this.getByState(STATE_INIT_ENTER) ||
|
||||||
this.getActive();
|
this.getActive();
|
||||||
|
|
||||||
|
// if not set, by default climb up the nav controllers if
|
||||||
|
// there isn't a previous view in this nav controller
|
||||||
|
if (isUndefined(opts.climbNav)) {
|
||||||
|
opts.climbNav = true;
|
||||||
|
}
|
||||||
return this.remove(this.indexOf(activeView), 1, opts);
|
return this.remove(this.indexOf(activeView), 1, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -717,6 +723,39 @@ export class NavController extends Ion {
|
|||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
// there is a view ready to leave, meaning that a transition needs
|
// there is a view ready to leave, meaning that a transition needs
|
||||||
// to happen and the previously active view is going to animate out
|
// to happen and the previously active view is going to animate out
|
||||||
|
|
||||||
|
// get the view thats ready to enter
|
||||||
|
let enteringView = this.getByState(STATE_INIT_ENTER);
|
||||||
|
|
||||||
|
if (!enteringView) {
|
||||||
|
// oh knows! no entering view to go to!
|
||||||
|
// if there is no previous view that would enter in this nav stack
|
||||||
|
// and the option is set to climb up the nav parent looking
|
||||||
|
// for the next nav we could transition to instead
|
||||||
|
if (opts.climbNav) {
|
||||||
|
let parentNav: NavController = this.parent;
|
||||||
|
while (parentNav) {
|
||||||
|
if (!parentNav['_tabs']) {
|
||||||
|
// Tabs can be a parent, but it is not a collection of views
|
||||||
|
// only we're looking for an actual NavController w/ stack of views
|
||||||
|
leavingView.willLeave();
|
||||||
|
|
||||||
|
return parentNav.pop(opts).then((rtnVal: boolean) => {
|
||||||
|
leavingView.didLeave();
|
||||||
|
return rtnVal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parentNav = parentNav.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's no previous view and there's no valid parent nav
|
||||||
|
// to climb to so this shouldn't actually remove the leaving
|
||||||
|
// view because there's nothing that would enter, eww
|
||||||
|
leavingView.state = STATE_ACTIVE;
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
let resolve;
|
let resolve;
|
||||||
let promise = new Promise(res => { resolve = res; });
|
let promise = new Promise(res => { resolve = res; });
|
||||||
|
|
||||||
@ -724,9 +763,6 @@ export class NavController extends Ion {
|
|||||||
opts.animation = leavingView.getTransitionName(opts.direction);
|
opts.animation = leavingView.getTransitionName(opts.direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the view thats ready to enter
|
|
||||||
let enteringView = this.getByState(STATE_INIT_ENTER);
|
|
||||||
|
|
||||||
// start the transition, fire resolve when done...
|
// start the transition, fire resolve when done...
|
||||||
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||||
// transition has completed!!
|
// transition has completed!!
|
||||||
@ -1226,6 +1262,13 @@ export class NavController extends Ion {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
for (var i = this._views.length - 1; i >= 0; i--) {
|
||||||
|
this._views[i].destroy();
|
||||||
|
}
|
||||||
|
this._views = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@ -1266,6 +1309,8 @@ export class NavController extends Ion {
|
|||||||
if (!hostViewRef.destroyed && index !== -1) {
|
if (!hostViewRef.destroyed && index !== -1) {
|
||||||
viewContainer.remove(index);
|
viewContainer.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view.setInstance(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
// a new ComponentRef has been created
|
// a new ComponentRef has been created
|
||||||
@ -1575,6 +1620,7 @@ export interface NavOptions {
|
|||||||
transitionDelay?: number;
|
transitionDelay?: number;
|
||||||
postLoad?: Function;
|
postLoad?: Function;
|
||||||
progressAnimation?: boolean;
|
progressAnimation?: boolean;
|
||||||
|
climbNav?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATE_ACTIVE = 'active';
|
const STATE_ACTIVE = 'active';
|
||||||
|
@ -16,7 +16,7 @@ import {ViewController} from './view-controller';
|
|||||||
selector: 'ion-nav'
|
selector: 'ion-nav'
|
||||||
})
|
})
|
||||||
export class NavRouter extends RouterOutlet {
|
export class NavRouter extends RouterOutlet {
|
||||||
private _activeViewId;
|
private _lastUrl: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
_elementRef: ElementRef,
|
_elementRef: ElementRef,
|
||||||
@ -33,11 +33,6 @@ export class NavRouter extends RouterOutlet {
|
|||||||
_nav.registerRouter(this);
|
_nav.registerRouter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* TODO
|
|
||||||
* @param {ComponentInstruction} instruction TODO
|
|
||||||
*/
|
|
||||||
activate(nextInstruction: ComponentInstruction): Promise<any> {
|
activate(nextInstruction: ComponentInstruction): Promise<any> {
|
||||||
var previousInstruction = this['_currentInstruction'];
|
var previousInstruction = this['_currentInstruction'];
|
||||||
this['_currentInstruction'] = nextInstruction;
|
this['_currentInstruction'] = nextInstruction;
|
||||||
@ -45,11 +40,17 @@ export class NavRouter extends RouterOutlet {
|
|||||||
var childRouter = this['_parentRouter'].childRouter(componentType);
|
var childRouter = this['_parentRouter'].childRouter(componentType);
|
||||||
|
|
||||||
// prevent double navigations to the same view
|
// prevent double navigations to the same view
|
||||||
var lastView = this._nav.last();
|
let instruction = new ResolvedInstruction(nextInstruction, null, null);
|
||||||
if (this._nav.isTransitioning() || lastView && lastView.componentType === componentType && lastView.data === nextInstruction.params) {
|
let url: string;
|
||||||
return Promise.resolve();
|
if (instruction) {
|
||||||
|
url = instruction.toRootUrl();
|
||||||
|
if (url === this._lastUrl) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.debug('NavRouter, activate:', componentType.name, 'url:', url);
|
||||||
|
|
||||||
// tell the NavController which componentType, and it's params, to navigate to
|
// tell the NavController which componentType, and it's params, to navigate to
|
||||||
return this._nav.push(componentType, nextInstruction.params);
|
return this._nav.push(componentType, nextInstruction.params);
|
||||||
}
|
}
|
||||||
@ -58,19 +59,13 @@ export class NavRouter extends RouterOutlet {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Ionic after a transition has completed.
|
|
||||||
* @param {string} direction The direction of the state change
|
|
||||||
* @param {ViewController} viewCtrl The entering ViewController
|
|
||||||
*/
|
|
||||||
stateChange(direction: string, viewCtrl: ViewController) {
|
stateChange(direction: string, viewCtrl: ViewController) {
|
||||||
// stateChange is called by Ionic's NavController
|
// stateChange is called by Ionic's NavController
|
||||||
// type could be "push" or "pop"
|
// type could be "push" or "pop"
|
||||||
// viewCtrl is Ionic's ViewController class, which has the properties "componentType" and "params"
|
// viewCtrl is Ionic's ViewController class, which has the properties "componentType" and "params"
|
||||||
|
|
||||||
// only do an update if there's an actual view change
|
// only do an update if there's an actual view change
|
||||||
if (!viewCtrl || this._activeViewId === viewCtrl.id) return;
|
if (!viewCtrl) return;
|
||||||
this._activeViewId = viewCtrl.id;
|
|
||||||
|
|
||||||
// get the best PathRecognizer for this view's componentType
|
// get the best PathRecognizer for this view's componentType
|
||||||
let pathRecognizer = this.getPathRecognizerByComponent(viewCtrl.componentType);
|
let pathRecognizer = this.getPathRecognizerByComponent(viewCtrl.componentType);
|
||||||
@ -81,16 +76,19 @@ export class NavRouter extends RouterOutlet {
|
|||||||
|
|
||||||
// create a ResolvedInstruction from the componentInstruction
|
// create a ResolvedInstruction from the componentInstruction
|
||||||
let instruction = new ResolvedInstruction(componentInstruction, null, null);
|
let instruction = new ResolvedInstruction(componentInstruction, null, null);
|
||||||
|
if (instruction) {
|
||||||
|
let url = instruction.toRootUrl();
|
||||||
|
if (url === this._lastUrl) return;
|
||||||
|
|
||||||
this['_parentRouter'].navigateByInstruction(instruction);
|
this._lastUrl = url;
|
||||||
|
|
||||||
|
this['_parentRouter'].navigateByInstruction(instruction);
|
||||||
|
|
||||||
|
console.debug('NavRouter, stateChange, name:', viewCtrl.name, 'id:', viewCtrl.id, 'url:', url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} componentType TODO
|
|
||||||
* @returns {TODO} TODO
|
|
||||||
*/
|
|
||||||
getPathRecognizerByComponent(componentType) {
|
getPathRecognizerByComponent(componentType) {
|
||||||
// given a componentType, figure out the best PathRecognizer to use
|
// given a componentType, figure out the best PathRecognizer to use
|
||||||
let rules = this['_parentRouter'].registry._rules;
|
let rules = this['_parentRouter'].registry._rules;
|
||||||
|
@ -1,8 +1,26 @@
|
|||||||
import {NavController, NavOptions, Config, ViewController} from '../../../../ionic/ionic';
|
import {NavController, Tabs, NavOptions, Config, ViewController} from '../../../../ionic/ionic';
|
||||||
|
|
||||||
export function run() {
|
export function run() {
|
||||||
describe('NavController', () => {
|
describe('NavController', () => {
|
||||||
|
|
||||||
|
describe('pop', () => {
|
||||||
|
|
||||||
|
it('should do nothing if its the first view in the stack', () => {
|
||||||
|
let view1 = new ViewController(Page1);
|
||||||
|
view1.state = STATE_ACTIVE;
|
||||||
|
nav._views = [view1];
|
||||||
|
|
||||||
|
expect(nav.length()).toBe(1);
|
||||||
|
|
||||||
|
nav.pop();
|
||||||
|
|
||||||
|
expect(nav.length()).toBe(1);
|
||||||
|
expect(nav.getByIndex(0).state).toBe(STATE_ACTIVE);
|
||||||
|
expect(nav.getByIndex(0).componentType).toBe(Page1);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('popToRoot', () => {
|
describe('popToRoot', () => {
|
||||||
|
|
||||||
it('should go back to root', () => {
|
it('should go back to root', () => {
|
||||||
@ -1022,10 +1040,15 @@ export function run() {
|
|||||||
class Page5 {}
|
class Page5 {}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
nav = mockNav();
|
||||||
|
});
|
||||||
|
|
||||||
|
function mockNav() {
|
||||||
let elementRef = {
|
let elementRef = {
|
||||||
nativeElement: document.createElement('div')
|
nativeElement: document.createElement('div')
|
||||||
};
|
};
|
||||||
nav = new NavController(null, null, config, null, elementRef, null, null, null, null, null);
|
|
||||||
|
let nav = new NavController(null, null, config, null, elementRef, null, null, null, null, null);
|
||||||
|
|
||||||
nav._keyboard = {
|
nav._keyboard = {
|
||||||
isOpen: function() {
|
isOpen: function() {
|
||||||
@ -1045,7 +1068,9 @@ export function run() {
|
|||||||
setElementClass: function(){},
|
setElementClass: function(){},
|
||||||
setElementStyle: function(){}
|
setElementStyle: function(){}
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
return nav;
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ export class Tab extends NavController {
|
|||||||
private _panelId: string;
|
private _panelId: string;
|
||||||
private _btnId: string;
|
private _btnId: string;
|
||||||
private _loaded: boolean;
|
private _loaded: boolean;
|
||||||
private _loadTimer: any;
|
private _loadTmr: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -181,7 +181,7 @@ export class Tab extends NavController {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
preload(wait: number) {
|
preload(wait: number) {
|
||||||
this._loadTimer = setTimeout(() => {
|
this._loadTmr = setTimeout(() => {
|
||||||
if (!this._loaded) {
|
if (!this._loaded) {
|
||||||
console.debug('Tabs, preload', this.id);
|
console.debug('Tabs, preload', this.id);
|
||||||
this.load({
|
this.load({
|
||||||
@ -247,7 +247,8 @@ export class Tab extends NavController {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
clearTimeout(this._loadTimer);
|
clearTimeout(this._loadTmr);
|
||||||
|
super.ngOnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ export class Tabs extends Ion {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
add(tab) {
|
add(tab: Tab) {
|
||||||
tab.id = this.id + '-' + (++this._ids);
|
tab.id = this.id + '-' + (++this._ids);
|
||||||
this._tabs.push(tab);
|
this._tabs.push(tab);
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,14 @@ class SignIn {
|
|||||||
})
|
})
|
||||||
class ChatPage {
|
class ChatPage {
|
||||||
constructor(private viewCtrl: ViewController) {}
|
constructor(private viewCtrl: ViewController) {}
|
||||||
|
|
||||||
|
onPageDidLoad() {
|
||||||
|
console.log('ChatPage, onPageDidLoad');
|
||||||
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('ChatPage, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -98,6 +106,10 @@ class TabsPage {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('TabsPage, onPageDidLeave');
|
console.log('TabsPage, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('TabsPage, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,6 +125,7 @@ class TabsPage {
|
|||||||
'<p><button id="goToTab1Page2" (click)="push()">Go to Tab 1, Page 2</button></p>' +
|
'<p><button id="goToTab1Page2" (click)="push()">Go to Tab 1, Page 2</button></p>' +
|
||||||
'<p><button (click)="logout()">Logout</button></p>' +
|
'<p><button (click)="logout()">Logout</button></p>' +
|
||||||
'<p><button (click)="favoritesTab()">Favorites Tab</button></p>' +
|
'<p><button (click)="favoritesTab()">Favorites Tab</button></p>' +
|
||||||
|
'<p><button (click)="goBack()">Go Back</button></p>' +
|
||||||
'<p>UserId: {{userId}}</p>' +
|
'<p>UserId: {{userId}}</p>' +
|
||||||
'<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>' +
|
'<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>' +
|
||||||
'<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>' +
|
'<f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f><f></f>' +
|
||||||
@ -129,6 +142,13 @@ class Tab1Page1 {
|
|||||||
this.nav.push(Tab1Page2)
|
this.nav.push(Tab1Page2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
console.log('go back begin');
|
||||||
|
this.nav.pop().then((val) => {
|
||||||
|
console.log('go back completed', val);
|
||||||
|
});;
|
||||||
|
}
|
||||||
|
|
||||||
favoritesTab() {
|
favoritesTab() {
|
||||||
this.tabs.select(1);
|
this.tabs.select(1);
|
||||||
}
|
}
|
||||||
@ -152,6 +172,10 @@ class Tab1Page1 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab1Page1, onPageDidLeave');
|
console.log('Tab1Page1, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab1Page1, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -189,6 +213,10 @@ class Tab1Page2 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab1Page2, onPageDidLeave');
|
console.log('Tab1Page2, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab1Page2, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -221,6 +249,10 @@ class Tab1Page3 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab1Page3, onPageDidLeave');
|
console.log('Tab1Page3, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab1Page3, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -261,6 +293,10 @@ class Tab2Page1 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab2Page1, onPageDidLeave');
|
console.log('Tab2Page1, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab2Page1, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -298,6 +334,10 @@ class Tab2Page2 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab2Page2, onPageDidLeave');
|
console.log('Tab2Page2, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab2Page2, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -330,6 +370,10 @@ class Tab2Page3 {
|
|||||||
onPageDidLeave() {
|
onPageDidLeave() {
|
||||||
console.log('Tab2Page3, onPageDidLeave');
|
console.log('Tab2Page3, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab2Page3, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -362,6 +406,9 @@ class Tab3Page1 {
|
|||||||
console.log('Tab3Page1, onPageDidLeave');
|
console.log('Tab3Page1, onPageDidLeave');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPageDidUnload() {
|
||||||
|
console.log('Tab3Page1, onPageDidUnload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user