diff --git a/src/ng/route.js b/src/ng/route.js index e2a9c6333743..58c488525fd7 100644 --- a/src/ng/route.js +++ b/src/ng/route.js @@ -21,7 +21,9 @@ 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 - * route definition. + * route definition. A path can contain single parameters prefixed with a colon or catch-all + * partials (multiple levels including slashes) if they are prefixed with an asterisk. + * * @param {Object} route Mapping information to be assigned to `$route.current` on route * match. * @@ -327,6 +329,12 @@ function $RouteProvider(){ if (regex.match(paramRegExp)) { regex = regex.replace(paramRegExp, "([^\\/]*)$1"); params.push(param); + } else { + var specialParamRegExp = new RegExp("\\*" + param + "([\\W])"); + if (regex.match(specialParamRegExp)) { + regex = regex.replace(specialParamRegExp, "(.*)$1"); + params.push(param); + } } } }); diff --git a/test/ng/routeSpec.js b/test/ng/routeSpec.js index 0e98be5fef94..6bb19b9d61cb 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', + {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').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').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) {