This repository was archived by the owner on Apr 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27.4k
feat($route): express style route matching #1745
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,20 +119,66 @@ 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) { | ||
var redirectPath = (path[path.length-1] == '/') | ||
? 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 }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: !!optional (no space) |
||
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 regex to match the url against | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. regexp (to be consistent with the rest of the file) |
||
* @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}); | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the backslash before /edit belong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i want it to read
/color/:color/largecode/:largecode*/edit
, but the*/
ends the comment. what should i do?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha! I missed that.