Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 7eafbb9

Browse files
lrlopezmhevery
authored andcommitted
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.
1 parent bb8448c commit 7eafbb9

File tree

2 files changed

+80
-7
lines changed

2 files changed

+80
-7
lines changed

src/ng/route.js

+23-7
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,18 @@ function $RouteProvider(){
2323
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
2424
* route definition.
2525
*
26-
* `path` can contain named groups starting with a colon (`:name`). All characters up to the
27-
* next slash are matched and stored in `$routeParams` under the given `name` when the route
28-
* matches.
26+
* * `path` can contain named groups starting with a colon (`:name`). All characters up
27+
* to the next slash are matched and stored in `$routeParams` under the given `name`
28+
* when the route matches.
29+
* * `path` can contain named groups starting with a star (`*name`). All characters are
30+
* eagerly stored in `$routeParams` under the given `name` when the route matches.
31+
*
32+
* For example, routes like `/color/:color/largecode/*largecode/edit` will match
33+
* `/color/brown/largecode/code/with/slashs/edit` and extract:
34+
*
35+
* * `color: brown`
36+
* * `largecode: code/with/slashs`.
37+
*
2938
*
3039
* @param {Object} route Mapping information to be assigned to `$route.current` on route
3140
* match.
@@ -341,21 +350,28 @@ function $RouteProvider(){
341350
// regex only once and then reuse it
342351

343352
// Escape regexp special characters.
344-
when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
353+
when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$';
345354
var regex = '',
346355
params = [],
347356
dst = {};
348357

349-
var re = /:(\w+)/g,
358+
var re = /\\([:*])(\w+)/g,
350359
paramMatch,
351360
lastMatchedIndex = 0;
352361

353362
while ((paramMatch = re.exec(when)) !== null) {
354363
// Find each :param in `when` and replace it with a capturing group.
355364
// Append all other sections of when unchanged.
356365
regex += when.slice(lastMatchedIndex, paramMatch.index);
357-
regex += '([^\\/]*)';
358-
params.push(paramMatch[1]);
366+
switch(paramMatch[1]) {
367+
case ':':
368+
regex += '([^\\/]*)';
369+
break;
370+
case '*':
371+
regex += '(.*)';
372+
break;
373+
}
374+
params.push(paramMatch[2]);
359375
lastMatchedIndex = re.lastIndex;
360376
}
361377
// Append trailing path part.

test/ng/routeSpec.js

+57
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,63 @@ describe('$route', function() {
5959
});
6060
});
6161

62+
it('should route and fire change event when catch-all params are used', function() {
63+
var log = '',
64+
lastRoute,
65+
nextRoute;
66+
67+
module(function($routeProvider) {
68+
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
69+
{controller: noop, templateUrl: 'Chapter.html'});
70+
$routeProvider.when('/Book2/:book/*highlight/Chapter/:chapter',
71+
{controller: noop, templateUrl: 'Chapter.html'});
72+
$routeProvider.when('/Blank', {});
73+
});
74+
inject(function($route, $location, $rootScope) {
75+
$rootScope.$on('$routeChangeStart', function(event, next, current) {
76+
log += 'before();';
77+
expect(current).toBe($route.current);
78+
lastRoute = current;
79+
nextRoute = next;
80+
});
81+
$rootScope.$on('$routeChangeSuccess', function(event, current, last) {
82+
log += 'after();';
83+
expect(current).toBe($route.current);
84+
expect(lastRoute).toBe(last);
85+
expect(nextRoute).toBe(current);
86+
});
87+
88+
$location.path('/Book1/Moby/Chapter/Intro/one/edit').search('p=123');
89+
$rootScope.$digest();
90+
$httpBackend.flush();
91+
expect(log).toEqual('before();after();');
92+
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
93+
94+
log = '';
95+
$location.path('/Blank').search('ignore');
96+
$rootScope.$digest();
97+
expect(log).toEqual('before();after();');
98+
expect($route.current.params).toEqual({ignore:true});
99+
100+
log = '';
101+
$location.path('/Book1/Moby/Chapter/Intro/one/two/edit').search('p=123');
102+
$rootScope.$digest();
103+
expect(log).toEqual('before();after();');
104+
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
105+
106+
log = '';
107+
$location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
108+
$rootScope.$digest();
109+
expect(log).toEqual('before();after();');
110+
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
111+
112+
log = '';
113+
$location.path('/NONE');
114+
$rootScope.$digest();
115+
expect(log).toEqual('before();after();');
116+
expect($route.current).toEqual(null);
117+
});
118+
});
62119

63120
it('should not change route when location is canceled', function() {
64121
module(function($routeProvider) {

0 commit comments

Comments
 (0)