diff options
| -rw-r--r-- | src/ng/route.js | 47 | ||||
| -rw-r--r-- | test/ng/routeParamsSpec.js | 12 | ||||
| -rw-r--r-- | test/ng/routeSpec.js | 32 |
3 files changed, 67 insertions, 24 deletions
diff --git a/src/ng/route.js b/src/ng/route.js index 361b8ac3..91e4adb3 100644 --- a/src/ng/route.js +++ b/src/ng/route.js @@ -20,8 +20,13 @@ function $RouteProvider(){ * * @param {string} path Route path (matched against `$location.path`). If `$location.path` * contains redundant trailing slash or is missing one, the route will still match and the - * `$location.path` will be updated to add or drop the trailing slash to exacly match the + * `$location.path` will be updated to add or drop the trailing slash to exactly match the * route definition. + * + * `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` when the route + * matches. + * * @param {Object} route Mapping information to be assigned to `$route.current` on route * match. * @@ -286,8 +291,7 @@ function $RouteProvider(){ * instance of the Controller. */ - var matcher = switchRouteMatcher, - forceReload = false, + var forceReload = false, $route = { routes: routes, @@ -315,21 +319,36 @@ function $RouteProvider(){ ///////////////////////////////////////////////////// + /** + * @param on {string} current url + * @param when {string} route when template to match the url against + * @return {?Object} + */ function switchRouteMatcher(on, when) { // TODO(i): this code is convoluted and inefficient, we should construct the route matching // regex only once and then reuse it - var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$', + + // Escape regexp special characters. + when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$'; + var regex = '', params = [], dst = {}; - forEach(when.split(/[^\w:]/), function(param) { - if (param && param.charAt(0) === ':') { - var paramRegExp = new RegExp(param + "([\\W])"); - if (regex.match(paramRegExp)) { - regex = regex.replace(paramRegExp, "([^\\/]*)$1"); - params.push(param.substr(1)); - } - } - }); + + 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); + regex += '([^\\/]*)'; + params.push(paramMatch[1]); + lastMatchedIndex = re.lastIndex; + } + // Append trailing path part. + regex += when.substr(lastMatchedIndex); + var match = on.match(new RegExp(regex)); if (match) { forEach(params, function(name, index) { @@ -418,7 +437,7 @@ function $RouteProvider(){ // Match a route var params, match; forEach(routes, function(route, path) { - if (!match && (params = matcher($location.path(), path))) { + if (!match && (params = switchRouteMatcher($location.path(), path))) { match = inherit(route, { params: extend({}, $location.search(), params), pathParams: params}); diff --git a/test/ng/routeParamsSpec.js b/test/ng/routeParamsSpec.js index 07c6d4f7..52fe8d2a 100644 --- a/test/ng/routeParamsSpec.js +++ b/test/ng/routeParamsSpec.js @@ -29,4 +29,16 @@ describe('$routeParams', function() { expect($routeParams).toEqual({bar:'barvalue', foo:'foovalue'}); }); }); + + it('should support route params not preceded by slashes', function() { + module(function($routeProvider) { + $routeProvider.when('/bar:barId/foo:fooId/', {}); + }); + + inject(function($rootScope, $route, $location, $routeParams) { + $location.path('/barbarvalue/foofoovalue/'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId: 'barvalue', fooId: 'foovalue'}); + }); + }); }); diff --git a/test/ng/routeSpec.js b/test/ng/routeSpec.js index 52fffa40..852f97b5 100644 --- a/test/ng/routeSpec.js +++ b/test/ng/routeSpec.js @@ -82,28 +82,40 @@ describe('$route', function() { }); - it('should match a route that contains special chars in the path', function() { - module(function($routeProvider) { - $routeProvider.when('/$test.23/foo(bar)/:baz', {templateUrl: 'test.html'}); - }); - inject(function($route, $location, $rootScope) { + describe('should match a route that contains special chars in the path', function() { + beforeEach(module(function($routeProvider) { + $routeProvider.when('/$test.23/foo*(bar)/:baz', {templateUrl: 'test.html'}); + })); + it('matches the full path', inject(function($route, $location, $rootScope) { $location.path('/test'); $rootScope.$digest(); expect($route.current).toBeUndefined(); + })); - $location.path('/$testX23/foo(bar)/222'); + it('matches literal .', inject(function($route, $location, $rootScope) { + $location.path('/$testX23/foo*(bar)/222'); $rootScope.$digest(); expect($route.current).toBeUndefined(); + })); - $location.path('/$test.23/foo(bar)/222'); + it('matches literal *', inject(function($route, $location, $rootScope) { + $location.path('/$test.23/foooo(bar)/222'); $rootScope.$digest(); - expect($route.current).toBeDefined(); + expect($route.current).toBeUndefined(); + })); - $location.path('/$test.23/foo\\(bar)/222'); + it('treats backslashes normally', inject(function($route, $location, $rootScope) { + $location.path('/$test.23/foo*\\(bar)/222'); $rootScope.$digest(); expect($route.current).toBeUndefined(); - }); + })); + + it('matches a URL with special chars', inject(function($route, $location, $rootScope) { + $location.path('/$test.23/foo*(bar)/222'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + })); }); |
