mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 11:41:20 +08:00
router updates
This commit is contained in:
@ -6,165 +6,189 @@ import {PathRecognizer} from './path-recognizer';
|
||||
|
||||
export class IonicRouter {
|
||||
constructor(config) {
|
||||
this._routes = {};
|
||||
this._routes = [];
|
||||
this._viewCtrls = [];
|
||||
this.config(config);
|
||||
}
|
||||
|
||||
app(app) {
|
||||
this._app = app;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
config(config) {
|
||||
if (config) {
|
||||
for (let routeName in config) {
|
||||
this.addRoute(routeName, config[routeName]);
|
||||
for (let i = 0; i < config.length; i++) {
|
||||
this.addRoute(config[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addRoute(routeName, routeConfig) {
|
||||
if (routeName && routeConfig && routeConfig.path) {
|
||||
this._routes[routeName] = new Route(routeName, routeConfig);
|
||||
addRoute(routeConfig) {
|
||||
if (routeConfig && routeConfig.path && routeConfig.component) {
|
||||
let route = new Route(routeConfig);
|
||||
if (routeConfig.root) {
|
||||
this.otherwise(routeName);
|
||||
this.otherwise(route);
|
||||
}
|
||||
this._routes.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
load(window, ionicApp, ionicConfig) {
|
||||
// create each of the state manager classes
|
||||
for (let name in stateManagerClasses) {
|
||||
stateManagers[name] = new stateManagerClasses[name](window, this, ionicApp, ionicConfig);
|
||||
}
|
||||
stateManagerClasses = {};
|
||||
|
||||
return new Promise(resolve => {
|
||||
this.getCurrentPath().then(path => {
|
||||
this.loadByPath(path, this.otherwise()).then(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadByPath(path, fallbackRoute) {
|
||||
return new Promise(resolve => {
|
||||
let self = this;
|
||||
let activeViewCtrl = self.activeViewController();
|
||||
let matchedRoute = self.match(path) || fallbackRoute;
|
||||
|
||||
function zoneLoad() {
|
||||
self._app.zone().run(() => {
|
||||
activeViewCtrl.push(matchedRoute.cls);
|
||||
self.lastPath(matchedRoute.path);
|
||||
resolve();
|
||||
}, err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (activeViewCtrl && matchedRoute) {
|
||||
|
||||
if (matchedRoute.cls) {
|
||||
zoneLoad();
|
||||
|
||||
} else if (matchedRoute.module) {
|
||||
System.import(matchedRoute.module).then(m => {
|
||||
if (m) {
|
||||
matchedRoute.cls = m[matchedRoute.name];
|
||||
zoneLoad();
|
||||
}
|
||||
}, err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentPath() {
|
||||
// check each of the state managers and the one with the
|
||||
// highest priority wins of knowing what path we are currently at
|
||||
return new Promise(resolve => {
|
||||
|
||||
let promises = [];
|
||||
for (let name in stateManagers) {
|
||||
promises.push(stateManagers[name].getCurrentPath());
|
||||
}
|
||||
|
||||
// when all the promises have resolved then see which one wins
|
||||
Promise.all(promises).then(results => {
|
||||
let rtnPath = null;
|
||||
let highestPriority = -1;
|
||||
let state = null;
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
state = results[i];
|
||||
if (state.path && state.priority > highestPriority) {
|
||||
rtnPath = state.path;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(rtnPath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stateChange(type, activeView) {
|
||||
if (activeView && activeView.cls) {
|
||||
|
||||
let routeConfig = activeView.cls.route;
|
||||
if (routeConfig) {
|
||||
let matchedRoute = this.match(routeConfig.path);
|
||||
|
||||
if (matchedRoute) {
|
||||
// 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(matchedRoute.path, type, activeView);
|
||||
stateManagers[name].stateChange(path, type, activeView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.lastPath(matchedRoute.path);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lastPath(val) {
|
||||
if (arguments.length) {
|
||||
this._lastPath = val;
|
||||
}
|
||||
return this._lastPath;
|
||||
}
|
||||
|
||||
match(path) {
|
||||
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;
|
||||
let highestSpecifity = 0;
|
||||
|
||||
for (let routeName in this._routes) {
|
||||
routeMatch = this._routes[routeName].match(path);
|
||||
for (let i = 0; i < this._routes.length; i++) {
|
||||
route = this._routes[i];
|
||||
routeMatch = route.match(path);
|
||||
|
||||
if (routeMatch.match && (!matchedRoute || routeMatch.specificity > highestSpecifity)) {
|
||||
matchedRoute = this._routes[routeName];
|
||||
highestSpecifity = routeMatch.specificity;
|
||||
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;
|
||||
|
||||
} else if (this._otherwise) {
|
||||
return this._routes[this._otherwise];
|
||||
}
|
||||
return this._otherwise
|
||||
}
|
||||
|
||||
addViewController(viewCtrl) {
|
||||
this._viewCtrls.push(viewCtrl);
|
||||
}
|
||||
|
||||
activeViewController() {
|
||||
viewController() {
|
||||
if (this._viewCtrls.length) {
|
||||
return this._viewCtrls[ this._viewCtrls.length - 1 ];
|
||||
}
|
||||
@ -185,35 +209,21 @@ let stateManagerClasses = {};
|
||||
let stateManagers = {};
|
||||
|
||||
|
||||
export class Routable {
|
||||
constructor(cls, routeConfig) {
|
||||
cls.route = routeConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Route {
|
||||
constructor(name, routeConfig) {
|
||||
this.name = name;
|
||||
this.cls = null;
|
||||
constructor(routeConfig) {
|
||||
util.extend(this, routeConfig);
|
||||
this.recognizer = new PathRecognizer(this.path);
|
||||
this.specificity = this.recognizer.specificity;
|
||||
|
||||
this.component.route = this;
|
||||
}
|
||||
|
||||
match(matchPath) {
|
||||
let routeMatch = new RouteMatch(this, matchPath);
|
||||
if (routeMatch) {
|
||||
return routeMatch;
|
||||
}
|
||||
return false;
|
||||
match(path) {
|
||||
return RegExpWrapper.firstMatch(this.recognizer.regex, path);
|
||||
}
|
||||
|
||||
generate(params) {
|
||||
return this.recognizer.generate(params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RouteMatch {
|
||||
constructor(route, matchPath) {
|
||||
this.route = route;
|
||||
this.specificity = route.recognizer.specificity;
|
||||
this.match = RegExpWrapper.firstMatch(route.recognizer.regex, matchPath);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user