aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis Ramón López2012-11-12 22:09:53 +0100
committerMisko Hevery2013-02-14 21:36:59 -0800
commit7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5 (patch)
treefd990d11f349ebc063f3891ab1bbfc3aaa675671
parentbb8448c011127306df08c7479b66e5afe7a0fa94 (diff)
downloadangular.js-7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5.tar.bz2
feat(routeProvider): Add support to catch-all parameters in routes
This allows routeProvider to accept parameters that matches substrings even when they contain slashes if they are prefixed with an asterisk instead of a colon. For example, routes like edit/color/:color/largecode/*largecode will match with something like this http://appdomain.com/edit/color/brown/largecode/code/with/slashs.
-rw-r--r--src/ng/route.js30
-rw-r--r--test/ng/routeSpec.js57
2 files changed, 80 insertions, 7 deletions
diff --git a/src/ng/route.js b/src/ng/route.js
index 971caa1c..afdac3fb 100644
--- a/src/ng/route.js
+++ b/src/ng/route.js
@@ -23,9 +23,18 @@ function $RouteProvider(){
* `$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.
+ * * `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.
+ * * `path` can contain named groups starting with a star (`*name`). All characters are
+ * eagerly stored in `$routeParams` under the given `name` when the route matches.
+ *
+ * For example, routes like `/color/:color/largecode/*largecode/edit` will match
+ * `/color/brown/largecode/code/with/slashs/edit` and extract:
+ *
+ * * `color: brown`
+ * * `largecode: code/with/slashs`.
+ *
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
@@ -341,12 +350,12 @@ function $RouteProvider(){
// regex only once and then reuse it
// Escape regexp special characters.
- when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
+ when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$';
var regex = '',
params = [],
dst = {};
- var re = /:(\w+)/g,
+ var re = /\\([:*])(\w+)/g,
paramMatch,
lastMatchedIndex = 0;
@@ -354,8 +363,15 @@ function $RouteProvider(){
// 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]);
+ switch(paramMatch[1]) {
+ case ':':
+ regex += '([^\\/]*)';
+ break;
+ case '*':
+ regex += '(.*)';
+ break;
+ }
+ params.push(paramMatch[2]);
lastMatchedIndex = re.lastIndex;
}
// Append trailing path part.
diff --git a/test/ng/routeSpec.js b/test/ng/routeSpec.js
index e8e63b77..d43dcfba 100644
--- a/test/ng/routeSpec.js
+++ b/test/ng/routeSpec.js
@@ -59,6 +59,63 @@ describe('$route', function() {
});
});
+ it('should route and fire change event when catch-all params are used', function() {
+ var log = '',
+ lastRoute,
+ nextRoute;
+
+ module(function($routeProvider) {
+ $routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
+ {controller: noop, templateUrl: 'Chapter.html'});
+ $routeProvider.when('/Book2/:book/*highlight/Chapter/:chapter',
+ {controller: noop, templateUrl: 'Chapter.html'});
+ $routeProvider.when('/Blank', {});
+ });
+ inject(function($route, $location, $rootScope) {
+ $rootScope.$on('$routeChangeStart', function(event, next, current) {
+ log += 'before();';
+ expect(current).toBe($route.current);
+ lastRoute = current;
+ nextRoute = next;
+ });
+ $rootScope.$on('$routeChangeSuccess', function(event, current, last) {
+ log += 'after();';
+ expect(current).toBe($route.current);
+ expect(lastRoute).toBe(last);
+ expect(nextRoute).toBe(current);
+ });
+
+ $location.path('/Book1/Moby/Chapter/Intro/one/edit').search('p=123');
+ $rootScope.$digest();
+ $httpBackend.flush();
+ expect(log).toEqual('before();after();');
+ expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
+
+ log = '';
+ $location.path('/Blank').search('ignore');
+ $rootScope.$digest();
+ expect(log).toEqual('before();after();');
+ expect($route.current.params).toEqual({ignore:true});
+
+ log = '';
+ $location.path('/Book1/Moby/Chapter/Intro/one/two/edit').search('p=123');
+ $rootScope.$digest();
+ expect(log).toEqual('before();after();');
+ expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
+
+ log = '';
+ $location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
+ $rootScope.$digest();
+ expect(log).toEqual('before();after();');
+ expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
+
+ log = '';
+ $location.path('/NONE');
+ $rootScope.$digest();
+ expect(log).toEqual('before();after();');
+ expect($route.current).toEqual(null);
+ });
+ });
it('should not change route when location is canceled', function() {
module(function($routeProvider) {