diff options
| author | joshrtay | 2012-12-26 21:45:11 -0800 |
|---|---|---|
| committer | James deBoer | 2013-08-12 11:04:37 -0700 |
| commit | 04cebcc133c8b433a3ac5f72ed19f3631778142b (patch) | |
| tree | 17c7307558e2c41624d1092a7e826a20967112ee /src/ngRoute | |
| parent | c173ca412878d537b18df01f39e400ea48a4b398 (diff) | |
| download | angular.js-04cebcc133c8b433a3ac5f72ed19f3631778142b.tar.bz2 | |
feat($route): express style route matching
Added new route matching capabilities:
- optional param
Changed route matching syntax:
- named wildcard
BREAKING CHANGE: the syntax for named wildcard parameters in routes
has changed from *wildcard to :wildcard*
To migrate the code, follow the example below. Here, *highlight becomes
:highlight*:
Before:
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
{controller: noop, templateUrl: 'Chapter.html'});
After:
$routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
{controller: noop, templateUrl: 'Chapter.html'});
Diffstat (limited to 'src/ngRoute')
| -rw-r--r-- | src/ngRoute/route.js | 129 |
1 files changed, 82 insertions, 47 deletions
diff --git a/src/ngRoute/route.js b/src/ngRoute/route.js index 82c6cf3b..d1d7bba6 100644 --- a/src/ngRoute/route.js +++ b/src/ngRoute/route.js @@ -35,11 +35,13 @@ function $RouteProvider(){ * * * `path` can contain named groups starting with a colon (`:name`). All characters up * to the next slash are matched and stored in `$routeParams` under the given `name` - * after the route is resolved. - * * `path` can contain named groups starting with a star (`*name`). All characters are - * eagerly stored in `$routeParams` under the given `name` after the route is resolved. + * when the route matches. + * * `path` can contain named groups starting with a colon and ending with a star (`:name*`). + * All characters are eagerly stored in `$routeParams` under the given `name` + * when the route matches. + * * `path` can contain optional named groups with a question mark (`:name?`). * - * For example, routes like `/color/:color/largecode/*largecode/edit` will match + * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match * `/color/brown/largecode/code/with/slashs/edit` and extract: * * * `color: brown` @@ -117,7 +119,11 @@ function $RouteProvider(){ * Adds a new route definition to the `$route` service. */ this.when = function(path, route) { - routes[path] = extend({reloadOnSearch: true, caseInsensitiveMatch: false}, route); + routes[path] = extend( + {reloadOnSearch: true}, + route, + path && pathRegExp(path, route) + ); // create redirection for trailing slashes if (path) { @@ -125,12 +131,54 @@ function $RouteProvider(){ ? path.substr(0, path.length-1) : path +'/'; - routes[redirectPath] = {redirectTo: path}; + routes[redirectPath] = extend( + {redirectTo: path}, + pathRegExp(redirectPath, route) + ); } return this; }; + /** + * @param path {string} path + * @param opts {Object} options + * @return {?Object} + * + * @description + * Normalizes the given path, returning a regular expression + * and the original path. + * + * Inspired by pathRexp in visionmedia/express/lib/utils.js. + */ + function pathRegExp(path, opts) { + var insensitive = opts.caseInsensitiveMatch, + ret = { + originalPath: path, + regexp: path + }, + keys = ret.keys = []; + + path = path + .replace(/([().])/g, '\\$1') + .replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){ + var optional = option === '?' ? option : null; + var star = option === '*' ? option : null; + keys.push({ name: key, optional: !!optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (star && '(.+)?' || '([^/]+)?') + ')' + + (optional || ''); + }) + .replace(/([\/$\*])/g, '\\$1'); + + ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : ''); + return ret; + } + /** * @ngdoc method * @name ngRoute.$routeProvider#otherwise @@ -362,50 +410,37 @@ function $RouteProvider(){ /** * @param on {string} current url - * @param when {string} route when template to match the url against - * @param whenProperties {Object} properties to define when's matching behavior + * @param route {Object} route regexp to match the url against * @return {?Object} + * + * @description + * Check if the route matches the current url. + * + * Inspired by match in + * visionmedia/express/lib/router/router.js. */ - function switchRouteMatcher(on, when, whenProperties) { - // TODO(i): this code is convoluted and inefficient, we should construct the route matching - // regex only once and then reuse it - - // Escape regexp special characters. - when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$'; - - var regex = '', - params = [], - dst = {}; - - var re = /\\([:*])(\w+)/g, - paramMatch, - lastMatchedIndex = 0; - - while ((paramMatch = re.exec(when)) !== null) { - // Find each :param in `when` and replace it with a capturing group. - // Append all other sections of when unchanged. - regex += when.slice(lastMatchedIndex, paramMatch.index); - switch(paramMatch[1]) { - case ':': - regex += '([^\\/]*)'; - break; - case '*': - regex += '(.*)'; - break; + function switchRouteMatcher(on, route) { + var keys = route.keys, + params = {}; + + if (!route.regexp) return null; + + var m = route.regexp.exec(on); + if (!m) return null; + + var N = 0; + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + + var val = 'string' == typeof m[i] + ? decodeURIComponent(m[i]) + : m[i]; + + if (key && val) { + params[key.name] = val; } - params.push(paramMatch[2]); - lastMatchedIndex = re.lastIndex; - } - // Append trailing path part. - regex += when.substr(lastMatchedIndex); - - var match = on.match(new RegExp(regex, whenProperties.caseInsensitiveMatch ? 'i' : '')); - if (match) { - forEach(params, function(name, index) { - dst[name] = match[index + 1]; - }); } - return match ? dst : null; + return params; } function updateRoute() { @@ -489,7 +524,7 @@ function $RouteProvider(){ // Match a route var params, match; forEach(routes, function(route, path) { - if (!match && (params = switchRouteMatcher($location.path(), path, route))) { + if (!match && (params = switchRouteMatcher($location.path(), route))) { match = inherit(route, { params: extend({}, $location.search(), params), pathParams: params}); |
