diff --git a/src/state.js b/src/state.js index 146cce228..24756a10b 100644 --- a/src/state.js +++ b/src/state.js @@ -778,6 +778,8 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { } if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'"); if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState); + if (!toState.params.$$validates(toParams)) return TransitionFailed; + var defaultParams = toState.params.$$values(); toParams = extend(defaultParams, toParams); to = toState; diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js index 524c2db81..ebda4ca82 100644 --- a/src/urlMatcherFactory.js +++ b/src/urlMatcherFactory.js @@ -94,6 +94,14 @@ function UrlMatcher(pattern, config) { return result + flag + '(' + pattern + ')' + flag; } + function regexpType(regexp) { + var type = new Type({ + pattern: new RegExp(regexp), + is: function(value) { return type.pattern.exec(type.encode(value)); } + }); + return type; + } + this.source = pattern; // Split into static segments separated by path parameter placeholders. @@ -104,7 +112,7 @@ function UrlMatcher(pattern, config) { id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*'); segment = pattern.substring(last, m.index); - type = this.$types[regexp] || new Type({ pattern: new RegExp(regexp) }); + type = this.$types[regexp] || regexpType(regexp); cfg = config.params[id]; if (segment.indexOf('?') >= 0) break; // we're into the search part diff --git a/test/stateSpec.js b/test/stateSpec.js index ed7c3a3a0..cd217dbfe 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -96,6 +96,9 @@ describe('state', function () { .state('badParam', { url: "/bad/{param:int}" }) + .state('badParam2', { + url: "/bad2/{param:[0-9]{5}}" + }) .state('first', { url: '^/first/subpath' }) .state('second', { url: '^/second' }) @@ -755,6 +758,7 @@ describe('state', function () { 'about.sidebar', 'about.sidebar.item', 'badParam', + 'badParam2', 'dynamicController', 'first', 'home', @@ -875,6 +879,29 @@ describe('state', function () { $rootScope.$apply(); expect($state.current.name).toBe("about"); })); + + it('should ignore bad state parameters', inject(function ($state, $rootScope, $location, $stateParams) { + $state.go("badParam", { param: 5 }); + $rootScope.$apply(); + expect($state.current.name).toBe("badParam"); + expect($stateParams).toEqual({param: 5}); + + $state.go("badParam2", { param: '12345' }); // must be 5 digits + $rootScope.$apply(); + expect($state.current.name).toBe("badParam2"); + + $state.go("about"); + $rootScope.$apply(); + expect($state.current.name).toBe('about'); + + $state.go("badParam", { param: 'foo' }); + $rootScope.$apply(); + expect($state.current.name).toBe("about"); + + $state.go("badParam2", { param: '1234' }); // must be 5 digits + $rootScope.$apply(); + expect($state.current.name).toBe("about"); + })); }); it('should revert to last known working url on state change failure', inject(function ($state, $rootScope, $location, $q) {