mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
ionic router update
This commit is contained in:
@ -24,6 +24,7 @@ export * from 'ionic/components/modal/modal'
|
|||||||
export * from 'ionic/components/nav/nav'
|
export * from 'ionic/components/nav/nav'
|
||||||
export * from 'ionic/components/nav/nav-controller'
|
export * from 'ionic/components/nav/nav-controller'
|
||||||
export * from 'ionic/components/nav/nav-push'
|
export * from 'ionic/components/nav/nav-push'
|
||||||
|
export * from 'ionic/components/nav/nav-router'
|
||||||
export * from 'ionic/components/nav-bar/nav-bar'
|
export * from 'ionic/components/nav-bar/nav-bar'
|
||||||
export * from 'ionic/components/popup/popup'
|
export * from 'ionic/components/popup/popup'
|
||||||
export * from 'ionic/components/slides/slides'
|
export * from 'ionic/components/slides/slides'
|
||||||
@ -36,3 +37,4 @@ export * from 'ionic/components/switch/switch'
|
|||||||
export * from 'ionic/components/tabs/tabs'
|
export * from 'ionic/components/tabs/tabs'
|
||||||
export * from 'ionic/components/tabs/tab'
|
export * from 'ionic/components/tabs/tab'
|
||||||
export * from 'ionic/components/toolbar/toolbar'
|
export * from 'ionic/components/toolbar/toolbar'
|
||||||
|
export * from 'ionic/components/view/view-item'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Component, View, bootstrap, ElementRef, NgZone, bind, DynamicComponentLoader, Injector} from 'angular2/angular2';
|
import {Component, View, bootstrap, ElementRef, NgZone, bind, DynamicComponentLoader, Injector} from 'angular2/angular2';
|
||||||
|
import {routerInjectables, HashLocationStrategy, LocationStrategy, Router} from 'angular2/router';
|
||||||
|
|
||||||
import {IonicRouter} from '../../routing/router';
|
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {Platform} from '../../platform/platform';
|
import {Platform} from '../../platform/platform';
|
||||||
import * as util from '../../util/util';
|
import * as util from '../../util/util';
|
||||||
@ -19,8 +19,6 @@ export class IonicApp {
|
|||||||
|
|
||||||
// Our component registry map
|
// Our component registry map
|
||||||
this.components = {};
|
this.components = {};
|
||||||
|
|
||||||
this._activeViewId = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load(appRef) {
|
load(appRef) {
|
||||||
@ -54,17 +52,6 @@ export class IonicApp {
|
|||||||
this._zone.run(fn);
|
this._zone.run(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
stateChange(type, activeView) {
|
|
||||||
if (this._activeViewId !== activeView.id) {
|
|
||||||
this.router.stateChange(type, activeView);
|
|
||||||
this._activeViewId = activeView.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateClear() {
|
|
||||||
this.router.stateClear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a known component with a key, for easy lookups later.
|
* Register a known component with a key, for easy lookups later.
|
||||||
*/
|
*/
|
||||||
@ -175,7 +162,7 @@ class RootAnchor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ionicBootstrap(rootComponentType, config, router) {
|
export function ionicBootstrap(rootComponentType, config) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
try {
|
try {
|
||||||
// get the user config, or create one if wasn't passed in
|
// get the user config, or create one if wasn't passed in
|
||||||
@ -197,15 +184,6 @@ export function ionicBootstrap(rootComponentType, config, router) {
|
|||||||
// prepare the ready promise to fire....when ready
|
// prepare the ready promise to fire....when ready
|
||||||
Platform.prepareReady(config);
|
Platform.prepareReady(config);
|
||||||
|
|
||||||
// setup router
|
|
||||||
if (typeof router !== IonicRouter) {
|
|
||||||
router = new IonicRouter(router);
|
|
||||||
}
|
|
||||||
router.app(app);
|
|
||||||
|
|
||||||
// TODO: don't wire these together
|
|
||||||
app.router = router;
|
|
||||||
|
|
||||||
// TODO: probs need a better way to inject global injectables
|
// TODO: probs need a better way to inject global injectables
|
||||||
let actionMenu = new ActionMenu(app, config);
|
let actionMenu = new ActionMenu(app, config);
|
||||||
let modal = new Modal(app, config);
|
let modal = new Modal(app, config);
|
||||||
@ -215,10 +193,11 @@ export function ionicBootstrap(rootComponentType, config, router) {
|
|||||||
let appBindings = Injector.resolve([
|
let appBindings = Injector.resolve([
|
||||||
bind(IonicApp).toValue(app),
|
bind(IonicApp).toValue(app),
|
||||||
bind(IonicConfig).toValue(config),
|
bind(IonicConfig).toValue(config),
|
||||||
bind(IonicRouter).toValue(router),
|
|
||||||
bind(ActionMenu).toValue(actionMenu),
|
bind(ActionMenu).toValue(actionMenu),
|
||||||
bind(Modal).toValue(modal),
|
bind(Modal).toValue(modal),
|
||||||
bind(Popup).toValue(popup)
|
bind(Popup).toValue(popup),
|
||||||
|
routerInjectables,
|
||||||
|
bind(LocationStrategy).toClass(HashLocationStrategy)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
bootstrap(rootComponentType, appBindings).then(appRef => {
|
bootstrap(rootComponentType, appBindings).then(appRef => {
|
||||||
@ -240,10 +219,7 @@ export function ionicBootstrap(rootComponentType, config, router) {
|
|||||||
console.error(err)
|
console.error(err)
|
||||||
});
|
});
|
||||||
|
|
||||||
router.load(window, app, config).then(() => {
|
|
||||||
// resolve that the app has loaded
|
|
||||||
resolve(app);
|
resolve(app);
|
||||||
});
|
|
||||||
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('ionicBootstrap', err);
|
console.error('ionicBootstrap', err);
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
|
|
||||||
it('should check apple via checkbox element click', function() {
|
it('should check apple, enable/check grape, submit form', function() {
|
||||||
element(by.css('#e2eAppleCheckbox')).click();
|
element(by.css('#e2eAppleCheckbox')).click();
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should enable/check grape via buttons and submit form', function() {
|
|
||||||
element(by.css('#e2eGrapeDisabled')).click();
|
element(by.css('#e2eGrapeDisabled')).click();
|
||||||
element(by.css('#e2eGrapeChecked')).click();
|
element(by.css('#e2eGrapeChecked')).click();
|
||||||
element(by.css('#e2eSubmit')).click();
|
element(by.css('#e2eSubmit')).click();
|
||||||
|
@ -37,7 +37,10 @@ export class NavController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NavParams {
|
export class NavParams {
|
||||||
constructor(params) {
|
constructor(data) {
|
||||||
extend(this, params);
|
this.data = data || {};
|
||||||
|
}
|
||||||
|
get(param) {
|
||||||
|
return this.data[param];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
81
ionic/components/nav/nav-router.ts
Normal file
81
ionic/components/nav/nav-router.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import {Directive, ElementRef, DynamicComponentLoader, Attribute} from 'angular2/angular2';
|
||||||
|
import {
|
||||||
|
RouterOutlet,
|
||||||
|
Router,
|
||||||
|
ComponentInstruction,
|
||||||
|
Instruction,
|
||||||
|
Location} from 'angular2/router';
|
||||||
|
|
||||||
|
import {Nav} from './nav';
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'ion-nav'
|
||||||
|
})
|
||||||
|
export class NavRouter extends RouterOutlet {
|
||||||
|
|
||||||
|
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
|
||||||
|
_parentRouter: Router, @Attribute('name') nameAttr: string,
|
||||||
|
nav: Nav) {
|
||||||
|
super(_elementRef, _loader, _parentRouter, nameAttr);
|
||||||
|
|
||||||
|
// Nav is Ionic's ViewController, which we injected into this class
|
||||||
|
this.nav = nav;
|
||||||
|
|
||||||
|
// register this router with Ionic's ViewController
|
||||||
|
// Ionic's ViewController will call this NavRouter's "stateChange"
|
||||||
|
// method when the ViewController has...changed its state
|
||||||
|
nav.registerRouter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_activate(instruction: ComponentInstruction): Promise<any> {
|
||||||
|
var previousInstruction = this._currentInstruction;
|
||||||
|
this._currentInstruction = instruction;
|
||||||
|
var componentType = instruction.componentType;
|
||||||
|
this.childRouter = this._parentRouter.childRouter(componentType);
|
||||||
|
|
||||||
|
// tell the ViewController which componentType, and it's params, to navigate to
|
||||||
|
this.nav.push(componentType, instruction.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
stateChange(type, viewItem) {
|
||||||
|
// stateChange is called by Ionic's ViewController
|
||||||
|
// type could be "push" or "pop"
|
||||||
|
// viewItem is Ionic's ViewItem class, which has the properties "componentType" and "params"
|
||||||
|
|
||||||
|
// only do an update if there's an actual view change
|
||||||
|
if (!viewItem || this._activeViewId === viewItem.id) return;
|
||||||
|
this._activeViewId = viewItem.id;
|
||||||
|
|
||||||
|
// get the best PathRecognizer for this view's componentType
|
||||||
|
let pathRecognizer = this.getPathRecognizerByComponent(viewItem.componentType);
|
||||||
|
if (pathRecognizer) {
|
||||||
|
|
||||||
|
// generate a componentInstruction from the view's PathRecognizer and params
|
||||||
|
let componentInstruction = pathRecognizer.generate(viewItem.params.data);
|
||||||
|
|
||||||
|
// create an Instruction from the componentInstruction
|
||||||
|
let instruction = new Instruction(componentInstruction, null);
|
||||||
|
|
||||||
|
// update the browser's URL
|
||||||
|
this._parentRouter.navigateInstruction(instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPathRecognizerByComponent(componentType) {
|
||||||
|
// given a componentType, figure out the best PathRecognizer to use
|
||||||
|
let rules = this._parentRouter.registry._rules;
|
||||||
|
|
||||||
|
let pathRecognizer = null;
|
||||||
|
rules.forEach((rule) => {
|
||||||
|
|
||||||
|
pathRecognizer = rule.matchers.find((matcherPathRecognizer) => {
|
||||||
|
return (matcherPathRecognizer.handler.componentType === componentType);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return pathRecognizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,25 +1,12 @@
|
|||||||
import {App} from 'ionic/ionic';
|
import {App, NavController} from 'ionic/ionic';
|
||||||
|
|
||||||
import {FirstPage} from './pages/first-page';
|
import {FirstPage} from './pages/first-page';
|
||||||
import {SecondPage} from './pages/second-page';
|
|
||||||
import {ThirdPage} from './pages/third-page';
|
|
||||||
|
|
||||||
|
|
||||||
@App({
|
@App({
|
||||||
routes: [
|
template: '<ion-nav [root]="root"></ion-nav>'
|
||||||
{
|
|
||||||
path: '/firstpage',
|
|
||||||
component: FirstPage,
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/secondpage',
|
|
||||||
component: SecondPage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/thirdpage',
|
|
||||||
component: ThirdPage,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
class MyApp {}
|
class E2EApp {
|
||||||
|
constructor() {
|
||||||
|
this.root = FirstPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
56
ionic/components/nav/test/routing/index.ts
Normal file
56
ionic/components/nav/test/routing/index.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {RouteConfig, Location} from 'angular2/router';
|
||||||
|
|
||||||
|
import {App, IonicView, NavParams, ViewItem} from 'ionic/ionic';
|
||||||
|
|
||||||
|
|
||||||
|
@IonicView({templateUrl: 'view1.html'})
|
||||||
|
class View1Cmp {
|
||||||
|
constructor(location: Location, viewItem: ViewItem) {
|
||||||
|
this.path = location.path();
|
||||||
|
this.viewItem = viewItem;
|
||||||
|
console.log(`View1Cmp, path: ${this.path}`);
|
||||||
|
}
|
||||||
|
viewDidEnter() {
|
||||||
|
this.windowHash = window.location.hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IonicView({templateUrl: 'view2.html'})
|
||||||
|
class View2Cmp {
|
||||||
|
constructor(location: Location, viewItem: ViewItem) {
|
||||||
|
this.path = location.path();
|
||||||
|
this.viewItem = viewItem;
|
||||||
|
console.log(`View2Cmp, path: ${this.path}`);
|
||||||
|
}
|
||||||
|
viewDidEnter() {
|
||||||
|
this.windowHash = window.location.hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@IonicView({templateUrl: 'view3.html'})
|
||||||
|
class View3Cmp {
|
||||||
|
constructor(params: NavParams, location: Location, viewItem: ViewItem) {
|
||||||
|
this.id = params.get('id');
|
||||||
|
this.path = location.path();
|
||||||
|
this.viewItem = viewItem;
|
||||||
|
console.log(`View3Cmp, path: ${this.path}, param id: ${this.id}`);
|
||||||
|
}
|
||||||
|
viewDidEnter() {
|
||||||
|
this.windowHash = window.location.hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@App()
|
||||||
|
@RouteConfig([
|
||||||
|
{ path: '/', component: View1Cmp, as: 'first' },
|
||||||
|
{ path: '/second', component: View2Cmp, as: 'second' },
|
||||||
|
{ path: '/third/:id', component: View3Cmp, as: 'third' }
|
||||||
|
])
|
||||||
|
class InboxApp {
|
||||||
|
constructor(location: Location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
}
|
22
ionic/components/nav/test/routing/view1.html
Normal file
22
ionic/components/nav/test/routing/view1.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
<ion-navbar *navbar primary>
|
||||||
|
<ion-title>First View</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
|
||||||
|
<ion-content class="padding">
|
||||||
|
<h4>
|
||||||
|
Window Hash: {{windowHash}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
Location Path: {{path}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
ViewItem Id: {{viewItem.id}}
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<a href="#/second">Second View via href</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#/third/3">Third View via href, 3 as id param</a>
|
||||||
|
</p>
|
||||||
|
</ion-content>
|
22
ionic/components/nav/test/routing/view2.html
Normal file
22
ionic/components/nav/test/routing/view2.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
<ion-navbar *navbar primary>
|
||||||
|
<ion-title>Second View</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
|
||||||
|
<ion-content class="padding">
|
||||||
|
<h4>
|
||||||
|
Window Hash: {{windowHash}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
Location Path: {{path}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
ViewItem Id: {{viewItem.id}}
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<a href="#/third/12">Third View via href, 12 as id param</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#/">First View via href</a>
|
||||||
|
</p>
|
||||||
|
</ion-content>
|
25
ionic/components/nav/test/routing/view3.html
Normal file
25
ionic/components/nav/test/routing/view3.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
<ion-navbar *navbar primary>
|
||||||
|
<ion-title>Third View</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
|
||||||
|
<ion-content class="padding">
|
||||||
|
<h4>
|
||||||
|
Window Hash: {{windowHash}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
Location Path: {{path}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
NavParams, id: {{id}}
|
||||||
|
</h4>
|
||||||
|
<h4>
|
||||||
|
ViewItem Id: {{viewItem.id}}
|
||||||
|
</h4>
|
||||||
|
<p>
|
||||||
|
<a href="#/second">Second View via href</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#/">First View via href</a>
|
||||||
|
</p>
|
||||||
|
</ion-content>
|
@ -1,4 +1,4 @@
|
|||||||
import {Component} from 'angular2/angular2';
|
import {RouteConfig, Location} from 'angular2/router';
|
||||||
|
|
||||||
import {App, IonicView, NavController} from 'ionic/ionic';
|
import {App, IonicView, NavController} from 'ionic/ionic';
|
||||||
|
|
||||||
@ -24,10 +24,10 @@ class SignIn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@IonicView({
|
@IonicView({
|
||||||
templateUrl: './tabs.html'
|
templateUrl: './tabs.html'
|
||||||
})
|
})
|
||||||
|
|
||||||
class TabsPage {
|
class TabsPage {
|
||||||
constructor(nav: NavController) {
|
constructor(nav: NavController) {
|
||||||
this.tab1Root = Tab1Page1
|
this.tab1Root = Tab1Page1
|
||||||
@ -184,13 +184,9 @@ class Tab2Page3 {
|
|||||||
class Tab3Page1 {}
|
class Tab3Page1 {}
|
||||||
|
|
||||||
|
|
||||||
@App({
|
@App()
|
||||||
routes: [
|
@RouteConfig([
|
||||||
{
|
{ path: '/', component: SignIn, as: 'signin' },
|
||||||
path: '/signin',
|
{ path: '/tabs', component: TabsPage, as: 'tabs' },
|
||||||
component: SignIn,
|
])
|
||||||
root: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
class E2EApp {}
|
class E2EApp {}
|
||||||
|
@ -5,7 +5,6 @@ import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
|||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {IonicApp} from '../app/app';
|
import {IonicApp} from '../app/app';
|
||||||
import {IonicRouter} from '../../routing/router';
|
|
||||||
import {ViewItem} from './view-item';
|
import {ViewItem} from './view-item';
|
||||||
import {NavController} from '../nav/nav-controller';
|
import {NavController} from '../nav/nav-controller';
|
||||||
import {PaneController} from '../nav/pane';
|
import {PaneController} from '../nav/pane';
|
||||||
@ -30,13 +29,10 @@ export class ViewController extends Ion {
|
|||||||
this.compiler = injector.get(Compiler);
|
this.compiler = injector.get(Compiler);
|
||||||
this.loader = injector.get(DynamicComponentLoader);
|
this.loader = injector.get(DynamicComponentLoader);
|
||||||
this.viewMngr = injector.get(AppViewManager);
|
this.viewMngr = injector.get(AppViewManager);
|
||||||
this.router = injector.get(IonicRouter);
|
|
||||||
this.app = injector.get(IonicApp);
|
this.app = injector.get(IonicApp);
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
|
|
||||||
this.router.addViewController(this);
|
|
||||||
|
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.panes = new PaneController(this);
|
this.panes = new PaneController(this);
|
||||||
|
|
||||||
@ -54,8 +50,8 @@ export class ViewController extends Ion {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
push(component, params = {}, opts = {}) {
|
push(componentType, params = {}, opts = {}) {
|
||||||
if (!component || this.isTransitioning()) {
|
if (!componentType || this.isTransitioning()) {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,13 +75,15 @@ export class ViewController extends Ion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a new ViewItem
|
// create a new ViewItem
|
||||||
let enteringItem = new ViewItem(this, component, params);
|
let enteringItem = new ViewItem(this, componentType, params);
|
||||||
|
|
||||||
// add the item to the stack
|
// add the item to the stack
|
||||||
this.add(enteringItem);
|
this.add(enteringItem);
|
||||||
|
|
||||||
// notify app of the state change
|
if (this.router) {
|
||||||
this.app.stateChange('push', enteringItem);
|
// notify router of the state change
|
||||||
|
this.router.stateChange('push', enteringItem, params);
|
||||||
|
}
|
||||||
|
|
||||||
// start the transition
|
// start the transition
|
||||||
this.transition(enteringItem, leavingItem, opts, () => {
|
this.transition(enteringItem, leavingItem, opts, () => {
|
||||||
@ -120,8 +118,10 @@ export class ViewController extends Ion {
|
|||||||
// only item on the history stack.
|
// only item on the history stack.
|
||||||
let enteringItem = this.getPrevious(leavingItem);
|
let enteringItem = this.getPrevious(leavingItem);
|
||||||
if (enteringItem) {
|
if (enteringItem) {
|
||||||
// notify app of the state change
|
if (this.router) {
|
||||||
this.app.stateChange('pop', enteringItem);
|
// notify router of the state change
|
||||||
|
this.router.stateChange('pop', enteringItem);
|
||||||
|
}
|
||||||
|
|
||||||
// start the transition
|
// start the transition
|
||||||
this.transition(enteringItem, leavingItem, opts, () => {
|
this.transition(enteringItem, leavingItem, opts, () => {
|
||||||
@ -145,8 +145,6 @@ export class ViewController extends Ion {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.app.stateClear();
|
|
||||||
|
|
||||||
// if animate has not been set then default to false
|
// if animate has not been set then default to false
|
||||||
opts.animate = opts.animate || false;
|
opts.animate = opts.animate || false;
|
||||||
|
|
||||||
@ -193,9 +191,9 @@ export class ViewController extends Ion {
|
|||||||
return this.push((component && component.component) || component, (component && component.params), opts);
|
return this.push((component && component.component) || component, (component && component.params), opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRoot(component, params = {}, opts = {}) {
|
setRoot(componentType, params = {}, opts = {}) {
|
||||||
return this.setItems([{
|
return this.setItems([{
|
||||||
component,
|
componentType,
|
||||||
params
|
params
|
||||||
}], opts);
|
}], opts);
|
||||||
}
|
}
|
||||||
@ -321,8 +319,10 @@ export class ViewController extends Ion {
|
|||||||
enteringItem.didEnter();
|
enteringItem.didEnter();
|
||||||
leavingItem.didLeave();
|
leavingItem.didLeave();
|
||||||
|
|
||||||
// notify app of the state change
|
if (this.router) {
|
||||||
this.app.stateChange('pop', enteringItem);
|
// notify router of the state change
|
||||||
|
this.router.stateChange('pop', enteringItem);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// cancelled the swipe back, return items to original state
|
// cancelled the swipe back, return items to original state
|
||||||
@ -550,6 +550,10 @@ export class ViewController extends Ion {
|
|||||||
return (item && item.state === STAGED_ENTERING_STATE);
|
return (item && item.state === STAGED_ENTERING_STATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerRouter(router) {
|
||||||
|
this.router = router;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTIVE_STATE = 1;
|
const ACTIVE_STATE = 1;
|
||||||
|
@ -2,7 +2,6 @@ import {CORE_DIRECTIVES, FORM_DIRECTIVES, Component, Directive, View, forwardRef
|
|||||||
|
|
||||||
import * as util from 'ionic/util';
|
import * as util from 'ionic/util';
|
||||||
import {IonicConfig} from './config';
|
import {IonicConfig} from './config';
|
||||||
import {IonicRouter} from '../routing/router';
|
|
||||||
import {ionicBootstrap} from '../components/app/app';
|
import {ionicBootstrap} from '../components/app/app';
|
||||||
import {
|
import {
|
||||||
Aside, Button, Content, Scroll, Refresher,
|
Aside, Button, Content, Scroll, Refresher,
|
||||||
@ -16,7 +15,7 @@ import {
|
|||||||
TextInput, TextInputElement, Label,
|
TextInput, TextInputElement, Label,
|
||||||
Segment, SegmentButton, SegmentControlValueAccessor,
|
Segment, SegmentButton, SegmentControlValueAccessor,
|
||||||
RadioGroup, RadioButton, SearchBar,
|
RadioGroup, RadioButton, SearchBar,
|
||||||
Nav, NavbarTemplate, Navbar, NavPush, NavPop,
|
Nav, NavbarTemplate, Navbar, NavPush, NavPop, NavRouter,
|
||||||
TapClick, TapDisabled,
|
TapClick, TapDisabled,
|
||||||
Register,
|
Register,
|
||||||
ShowWhen, HideWhen,
|
ShowWhen, HideWhen,
|
||||||
@ -78,6 +77,7 @@ export const IonicDirectives = [
|
|||||||
forwardRef(() => Navbar),
|
forwardRef(() => Navbar),
|
||||||
forwardRef(() => NavPush),
|
forwardRef(() => NavPush),
|
||||||
forwardRef(() => NavPop),
|
forwardRef(() => NavPop),
|
||||||
|
forwardRef(() => NavRouter),
|
||||||
forwardRef(() => Register),
|
forwardRef(() => Register),
|
||||||
|
|
||||||
forwardRef(() => ShowWhen),
|
forwardRef(() => ShowWhen),
|
||||||
@ -172,7 +172,7 @@ export function App(args={}) {
|
|||||||
// redefine with added annotations
|
// redefine with added annotations
|
||||||
Reflect.defineMetadata('annotations', annotations, cls);
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
|
||||||
ionicBootstrap(cls, args.config, args.routes);
|
ionicBootstrap(cls, args.config);
|
||||||
|
|
||||||
return cls;
|
return cls;
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,6 @@ export * from './components'
|
|||||||
export * from './platform/platform'
|
export * from './platform/platform'
|
||||||
export * from './platform/registry'
|
export * from './platform/registry'
|
||||||
|
|
||||||
export * from './routing/router'
|
|
||||||
export * from './routing/url-state'
|
|
||||||
|
|
||||||
export * from './util/click-block'
|
export * from './util/click-block'
|
||||||
export * from './util/focus'
|
export * from './util/focus'
|
||||||
|
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
import {
|
|
||||||
RegExp,
|
|
||||||
RegExpWrapper,
|
|
||||||
RegExpMatcherWrapper,
|
|
||||||
StringWrapper,
|
|
||||||
isPresent,
|
|
||||||
isBlank,
|
|
||||||
BaseException,
|
|
||||||
normalizeBlank
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
import {
|
|
||||||
Map,
|
|
||||||
MapWrapper,
|
|
||||||
StringMap,
|
|
||||||
StringMapWrapper,
|
|
||||||
List,
|
|
||||||
ListWrapper
|
|
||||||
} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
|
|
||||||
class ContinuationSegment {
|
|
||||||
generate(params) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StaticSegment {
|
|
||||||
constructor(string) {
|
|
||||||
this.name = '';
|
|
||||||
this.regex = escapeRegex(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
generate() {
|
|
||||||
return this.regex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DynamicSegment {
|
|
||||||
constructor(name) {
|
|
||||||
this.regex = "([^/]+)";
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(params) {
|
|
||||||
if (!StringMapWrapper.contains(params, this.name)) {
|
|
||||||
throw new BaseException(
|
|
||||||
`Route generator for '${this.name}' was not included in parameters passed.`)
|
|
||||||
}
|
|
||||||
return normalizeBlank(StringMapWrapper.get(params, this.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StarSegment {
|
|
||||||
constructor(name) {
|
|
||||||
this.regex = "(.+)";
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(params) {
|
|
||||||
return normalizeBlank(StringMapWrapper.get(params, this.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var paramMatcher = RegExpWrapper.create("^:([^\/]+)$");
|
|
||||||
var wildcardMatcher = RegExpWrapper.create("^\\*([^\/]+)$");
|
|
||||||
|
|
||||||
function parsePathString(route: string) {
|
|
||||||
// normalize route as not starting with a "/". Recognition will
|
|
||||||
// also normalize.
|
|
||||||
if (StringWrapper.startsWith(route, "/")) {
|
|
||||||
route = StringWrapper.substring(route, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var segments = splitBySlash(route);
|
|
||||||
var results = [];
|
|
||||||
var specificity = 0;
|
|
||||||
|
|
||||||
// The "specificity" of a path is used to determine which route is used when multiple routes match
|
|
||||||
// a URL.
|
|
||||||
// Static segments (like "/foo") are the most specific, followed by dynamic segments (like
|
|
||||||
// "/:id"). Star segments
|
|
||||||
// add no specificity. Segments at the start of the path are more specific than proceeding ones.
|
|
||||||
// The code below uses place values to combine the different types of segments into a single
|
|
||||||
// integer that we can
|
|
||||||
// sort later. Each static segment is worth hundreds of points of specificity (10000, 9900, ...,
|
|
||||||
// 200), and each
|
|
||||||
// dynamic segment is worth single points of specificity (100, 99, ... 2).
|
|
||||||
if (segments.length > 98) {
|
|
||||||
throw new BaseException(`'${route}' has more than the maximum supported number of segments.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
var limit = segments.length - 1;
|
|
||||||
for (var i = 0; i <= limit; i++) {
|
|
||||||
var segment = segments[i], match;
|
|
||||||
|
|
||||||
if (isPresent(match = RegExpWrapper.firstMatch(paramMatcher, segment))) {
|
|
||||||
results.push(new DynamicSegment(match[1]));
|
|
||||||
specificity += (100 - i);
|
|
||||||
} else if (isPresent(match = RegExpWrapper.firstMatch(wildcardMatcher, segment))) {
|
|
||||||
results.push(new StarSegment(match[1]));
|
|
||||||
} else if (segment == '...') {
|
|
||||||
if (i < limit) {
|
|
||||||
// TODO (matsko): setup a proper error here `
|
|
||||||
throw new BaseException(`Unexpected "..." before the end of the path for "${route}".`);
|
|
||||||
}
|
|
||||||
results.push(new ContinuationSegment());
|
|
||||||
} else if (segment.length > 0) {
|
|
||||||
results.push(new StaticSegment(segment));
|
|
||||||
specificity += 100 * (100 - i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {segments: results, specificity};
|
|
||||||
}
|
|
||||||
|
|
||||||
function splitBySlash(url: string): List<string> {
|
|
||||||
return url.split('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// represents something like '/foo/:bar'
|
|
||||||
export class PathRecognizer {
|
|
||||||
|
|
||||||
constructor(path) {
|
|
||||||
this.segments = [];
|
|
||||||
|
|
||||||
var parsed = parsePathString(path);
|
|
||||||
var specificity = parsed['specificity'];
|
|
||||||
var segments = parsed['segments'];
|
|
||||||
var regexString = '^';
|
|
||||||
|
|
||||||
ListWrapper.forEach(segments, (segment) => {
|
|
||||||
if (segment instanceof ContinuationSegment) {
|
|
||||||
this.terminal = false;
|
|
||||||
} else {
|
|
||||||
regexString += '/' + segment.regex;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.terminal) {
|
|
||||||
regexString += '$';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.regex = RegExpWrapper.create(regexString);
|
|
||||||
this.segments = segments;
|
|
||||||
this.specificity = specificity;
|
|
||||||
}
|
|
||||||
|
|
||||||
parseParams(url) {
|
|
||||||
var params = StringMapWrapper.create();
|
|
||||||
var urlPart = url;
|
|
||||||
for (var i = 0; i < this.segments.length; i++) {
|
|
||||||
var segment = this.segments[i];
|
|
||||||
if (segment instanceof ContinuationSegment) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = RegExpWrapper.firstMatch(RegExpWrapper.create('/' + segment.regex), urlPart);
|
|
||||||
urlPart = StringWrapper.substring(urlPart, match[0].length);
|
|
||||||
if (segment.name.length > 0) {
|
|
||||||
StringMapWrapper.set(params, segment.name, match[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(params) {
|
|
||||||
return ListWrapper.join(
|
|
||||||
ListWrapper.map(this.segments, (segment) => '/' + segment.generate(params)), '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var specialCharacters = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
|
|
||||||
var escapeRe = RegExpWrapper.create('(\\' + specialCharacters.join('|\\') + ')', 'g');
|
|
||||||
|
|
||||||
function escapeRegex(string): string {
|
|
||||||
return StringWrapper.replaceAllMapped(string, escapeRe, (match) => { return "\\" + match; });
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
import {RegExpWrapper} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
import * as util from '../util/util';
|
|
||||||
import {PathRecognizer} from './path-recognizer';
|
|
||||||
|
|
||||||
|
|
||||||
export class IonicRouter {
|
|
||||||
constructor(config) {
|
|
||||||
this._routes = [];
|
|
||||||
this._viewCtrls = [];
|
|
||||||
this.config(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
app(app) {
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
config(config) {
|
|
||||||
if (config) {
|
|
||||||
for (let i = 0; i < config.length; i++) {
|
|
||||||
this.addRoute(config[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addRoute(routeConfig) {
|
|
||||||
if (routeConfig && routeConfig.path && routeConfig.component) {
|
|
||||||
let route = new Route(routeConfig);
|
|
||||||
if (routeConfig.root) {
|
|
||||||
this.otherwise(route);
|
|
||||||
}
|
|
||||||
this._routes.push(route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChange(type, activeView) {
|
|
||||||
// this fires when the app's state has changed. `stateChange` will
|
|
||||||
// tell each of the state managers that the state has changed, and
|
|
||||||
// each state manager will decide what to do with this info
|
|
||||||
// (the url state manager updates the url bar if a route was setup)
|
|
||||||
if (activeView && activeView.component) {
|
|
||||||
|
|
||||||
let componentRoute = activeView.component.route;
|
|
||||||
if (componentRoute) {
|
|
||||||
let path = componentRoute.generate(activeView.params);
|
|
||||||
if (path) {
|
|
||||||
for (let name in stateManagers) {
|
|
||||||
stateManagers[name].stateChange(path, type, activeView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateClear() {
|
|
||||||
for (let name in stateManagers) {
|
|
||||||
stateManagers[name].stateClear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matchPaths(paths) {
|
|
||||||
// load each of paths to a component
|
|
||||||
let components = [];
|
|
||||||
let route;
|
|
||||||
|
|
||||||
if (paths) {
|
|
||||||
for (let i = 0; i < paths.length; i++) {
|
|
||||||
route = this.matchPath(paths[i]);
|
|
||||||
if (route && route.component) {
|
|
||||||
components.push(route.component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
matchPath(path) {
|
|
||||||
// takes a string path and loops through each of the setup
|
|
||||||
// routes to see if the path matches any of the routes
|
|
||||||
// the matched path with the highest specifity wins
|
|
||||||
let matchedRoute = null;
|
|
||||||
let route = null;
|
|
||||||
let routeMatch = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < this._routes.length; i++) {
|
|
||||||
route = this._routes[i];
|
|
||||||
routeMatch = route.match(path);
|
|
||||||
|
|
||||||
if (routeMatch && (!matchedRoute || route.specificity > matchedRoute.specificity)) {
|
|
||||||
matchedRoute = route;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchedRoute;
|
|
||||||
}
|
|
||||||
|
|
||||||
load(window, ionicApp, ionicConfig) {
|
|
||||||
// load is called when the app has finished loading each state
|
|
||||||
// manager gets a chance to say what path the app should be at
|
|
||||||
let viewCtrl = this.viewController();
|
|
||||||
if (!viewCtrl || !this._routes.length) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolve;
|
|
||||||
let promise = new Promise(res => { resolve = res; });
|
|
||||||
|
|
||||||
// get the initial load paths from the state manager with the highest priorty
|
|
||||||
this.getManagerPaths(window, ionicApp, ionicConfig).then(paths => {
|
|
||||||
|
|
||||||
// load all of the paths the highest priority state manager has given
|
|
||||||
let components = this.matchPaths(paths);
|
|
||||||
|
|
||||||
if (!components.length && this.otherwise()) {
|
|
||||||
// the state manager did not find and loaded components
|
|
||||||
// use the "otherwise" path
|
|
||||||
components = [this.otherwise().component];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.app.zoneRun(() => {
|
|
||||||
viewCtrl.setItems(components).then(resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
getManagerPaths(window, ionicApp, ionicConfig) {
|
|
||||||
// loop through all of the state managers and load their paths
|
|
||||||
// the state manager with valid paths and highest priority wins
|
|
||||||
let resolve;
|
|
||||||
let promise = new Promise(res => { resolve = res; });
|
|
||||||
|
|
||||||
// load each of the state managers
|
|
||||||
let stateManagerPromises = [];
|
|
||||||
for (let name in stateManagerClasses) {
|
|
||||||
stateManagers[name] = new stateManagerClasses[name](window, this, ionicApp, ionicConfig);
|
|
||||||
stateManagerPromises.push( stateManagers[name].load() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// when all the state manager loads have resolved then see which one wins
|
|
||||||
Promise.all(stateManagerPromises).then(stateManagerLoadResults => {
|
|
||||||
|
|
||||||
// now that all the state managers are loaded
|
|
||||||
// get the highest priority state manager's paths
|
|
||||||
let stateLoadResult = null;
|
|
||||||
let paths = null;
|
|
||||||
let highestPriority = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i < stateManagerLoadResults.length; i++) {
|
|
||||||
stateLoadResult = stateManagerLoadResults[i];
|
|
||||||
if (stateLoadResult && stateLoadResult.paths.length && stateLoadResult.priority > highestPriority) {
|
|
||||||
paths = stateLoadResult.paths;
|
|
||||||
highestPriority = stateLoadResult.priority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(paths);
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
push(path) {
|
|
||||||
let viewCtrl = this.viewController();
|
|
||||||
if (viewCtrl) {
|
|
||||||
let matchedRoute = this.matchPath(path);
|
|
||||||
if (matchedRoute && matchedRoute.component) {
|
|
||||||
this.app.zoneRun(() => {
|
|
||||||
viewCtrl.push(matchedRoute.component, matchedRoute.params, {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pop() {
|
|
||||||
let viewCtrl = this.viewController();
|
|
||||||
if (viewCtrl) {
|
|
||||||
this.app.zoneRun(() => {
|
|
||||||
viewCtrl.pop();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
otherwise(val) {
|
|
||||||
if (arguments.length) {
|
|
||||||
this._otherwise = val;
|
|
||||||
}
|
|
||||||
return this._otherwise
|
|
||||||
}
|
|
||||||
|
|
||||||
addViewController(viewCtrl) {
|
|
||||||
this._viewCtrls.push(viewCtrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
viewController() {
|
|
||||||
if (this._viewCtrls.length) {
|
|
||||||
return this._viewCtrls[ this._viewCtrls.length - 1 ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static registerStateManager(name, StateManagerClass) {
|
|
||||||
stateManagerClasses[name] = StateManagerClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
static deregisterStateManager(name) {
|
|
||||||
delete stateManagerClasses[name];
|
|
||||||
delete stateManagers[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let stateManagerClasses = {};
|
|
||||||
let stateManagers = {};
|
|
||||||
|
|
||||||
|
|
||||||
class Route {
|
|
||||||
constructor(routeConfig) {
|
|
||||||
util.extend(this, routeConfig);
|
|
||||||
this.recognizer = new PathRecognizer(this.path);
|
|
||||||
this.specificity = this.recognizer.specificity;
|
|
||||||
|
|
||||||
this.component.route = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
match(path) {
|
|
||||||
return RegExpWrapper.firstMatch(this.recognizer.regex, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(params) {
|
|
||||||
return this.recognizer.generate(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
import {IonicRouter} from './router';
|
|
||||||
import * as util from '../util/util';
|
|
||||||
|
|
||||||
|
|
||||||
class UrlStateManager {
|
|
||||||
|
|
||||||
constructor(window, router) {
|
|
||||||
this.location = window.location;
|
|
||||||
this.history = window.history;
|
|
||||||
this.ls = window.localStorage;
|
|
||||||
this.router = router;
|
|
||||||
|
|
||||||
// overkill for location change listeners, but ensures we
|
|
||||||
// know when the location has changed. Only 1 of the listeners
|
|
||||||
// will actually do the work, the other will be skipped.
|
|
||||||
window.addEventListener('popstate', () => {
|
|
||||||
this.onLocationChange();
|
|
||||||
});
|
|
||||||
window.addEventListener('hashchange', () => {
|
|
||||||
this.onLocationChange();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
load() {
|
|
||||||
let paths = [this.getCurrentPath()];
|
|
||||||
let savedPaths = this.paths();
|
|
||||||
|
|
||||||
if (savedPaths[savedPaths.length - 1] == paths[0]) {
|
|
||||||
// the last path in the saved paths is the same as the
|
|
||||||
// current path, so use the saved paths to rebuild the history
|
|
||||||
paths = savedPaths;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// the current path is not the same as the last path in the
|
|
||||||
// saved history, so the saved history is no good, erase it
|
|
||||||
this.paths([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve({
|
|
||||||
paths: paths,
|
|
||||||
priority: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stateChange(path, type, activeView) {
|
|
||||||
let savedPaths = this.paths();
|
|
||||||
|
|
||||||
// check if the given path is different than the current location
|
|
||||||
let isDifferentPath = (this.getCurrentPath() !== path);
|
|
||||||
|
|
||||||
if (type == 'pop') {
|
|
||||||
// if the popstate came from the browser's back button (and not Ionic)
|
|
||||||
// then we shouldn't force another browser history.back()
|
|
||||||
// only do a history.back() if the URL hasn't been updated yet
|
|
||||||
if (isDifferentPath) {
|
|
||||||
this.history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedPaths.length && savedPaths[savedPaths.length - 1] != path) {
|
|
||||||
// only if the last item in the saved paths
|
|
||||||
// equals this path then it can be removed
|
|
||||||
savedPaths.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (this._hasInit) {
|
|
||||||
if (isDifferentPath) {
|
|
||||||
// push the new state to the history stack since the path
|
|
||||||
// isn't already in the location hash
|
|
||||||
this.history.pushState(path, '', '#' + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// replace the very first load with the correct entering state info
|
|
||||||
this.history.replaceState(path, '', '#' + path);
|
|
||||||
this._hasInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedPaths[savedPaths.length - 1] != path) {
|
|
||||||
// only if the last item in the saved paths does
|
|
||||||
// not equal this path then it can be added
|
|
||||||
savedPaths.push(path);
|
|
||||||
|
|
||||||
// don't allow the history to grow too large
|
|
||||||
if (savedPaths.length > MAX_PATH_STORE) {
|
|
||||||
savedPaths = savedPaths.slice( savedPaths.length - MAX_PATH_STORE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the new path data
|
|
||||||
this.paths(savedPaths);
|
|
||||||
|
|
||||||
// ensure this resets
|
|
||||||
this._currentPath = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
stateClear() {
|
|
||||||
this.paths([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
onLocationChange() {
|
|
||||||
let currentPath = this.getCurrentPath();
|
|
||||||
|
|
||||||
if (currentPath == this._currentPath) {
|
|
||||||
// absolutely no change since last onLocationChange
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep in-memory the current path to quickly tell if things have changed
|
|
||||||
this._currentPath = currentPath;
|
|
||||||
|
|
||||||
// load up the saved paths
|
|
||||||
let savedPaths = this.paths();
|
|
||||||
|
|
||||||
if (currentPath === savedPaths[savedPaths.length - 1]) {
|
|
||||||
// do nothing if the last saved path is
|
|
||||||
// the same as the current path
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPath === savedPaths[savedPaths.length - 2]) {
|
|
||||||
// the user is moving back
|
|
||||||
this.router.pop();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// the user is moving forward
|
|
||||||
this.router.push(currentPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paths(val) {
|
|
||||||
if (arguments.length) {
|
|
||||||
// set in-memory data
|
|
||||||
this._paths = val;
|
|
||||||
|
|
||||||
// set localStorage data
|
|
||||||
try {
|
|
||||||
this.ls.setItem(PATH_STORE_KEY, JSON.stringify(val));
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (!this._paths) {
|
|
||||||
// we don't already have data in-memory
|
|
||||||
|
|
||||||
// see if we have data in localStorage
|
|
||||||
try {
|
|
||||||
let strData = this.ls.getItem(PATH_STORE_KEY);
|
|
||||||
if (strData) {
|
|
||||||
this._paths = JSON.parse(strData);
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// if not in localStorage yet then create new path data
|
|
||||||
if (!this._paths) {
|
|
||||||
this._paths = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the in-memory data
|
|
||||||
return this._paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentPath() {
|
|
||||||
// remove leading # to get the path
|
|
||||||
return this.location.hash.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const PATH_STORE_KEY = 'ionic:history';
|
|
||||||
const MAX_PATH_STORE = 20;
|
|
||||||
|
|
||||||
IonicRouter.registerStateManager('url', UrlStateManager);
|
|
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
'node_modules/systemjs/node_modules/es6-module-loader/dist/es6-module-loader.js',
|
'node_modules/systemjs/node_modules/es6-module-loader/dist/es6-module-loader.js',
|
||||||
'node_modules/systemjs/dist/system.js',
|
'node_modules/systemjs/dist/system.js',
|
||||||
'node_modules/angular2/bundles/angular2.dev.js',
|
'node_modules/angular2/bundles/angular2.dev.js',
|
||||||
|
'node_modules/angular2/bundles/router.dev.js',
|
||||||
'dist/js/ionic.js',
|
'dist/js/ionic.js',
|
||||||
'node_modules/web-animations-js/web-animations.min.js'
|
'node_modules/web-animations-js/web-animations.min.js'
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user