diff --git a/src/urlMatcherFactory.js b/src/urlMatcherFactory.js index 3b733f9ae..07c05bff9 100644 --- a/src/urlMatcherFactory.js +++ b/src/urlMatcherFactory.js @@ -122,7 +122,11 @@ function UrlMatcher(pattern, config, parentMatcher) { cfg = config.params[id]; segment = pattern.substring(last, m.index); regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null); - type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) }); + + if (regexp) { + type = $$UMFP.type(regexp) || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) }); + } + return { id: id, regexp: regexp, segment: segment, type: type, cfg: cfg }; @@ -920,7 +924,12 @@ function $UrlMatcherFactory() { if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations."); if (urlType) return urlType; if (!config.type) return (location === "config" ? $types.any : $types.string); - return config.type instanceof Type ? config.type : new Type(config.type); + + if (angular.isString(config.type)) + return $types[config.type]; + if (config.type instanceof Type) + return config.type; + return new Type(config.type); } // array config: param name (param[]) overrides default settings. explicit config overrides param name. diff --git a/test/stateSpec.js b/test/stateSpec.js index 7249895bf..cecde231c 100644 --- a/test/stateSpec.js +++ b/test/stateSpec.js @@ -32,6 +32,7 @@ describe('state', function () { RSP = { url: '^/:doReload/search?term', reloadOnSearch: false }, OPT = { url: '/opt/:param', params: { param: "100" } }, OPT2 = { url: '/opt2/:param2/:param3', params: { param3: "300", param4: "400" } }, + URLLESS = { url: '/urllessparams', params: { myparam: { type: 'int' } } }, ISS2101 = { params: { bar: { squash: false, value: 'qux'}}, url: '/2101/{bar:string}' }; AppInjectable = {}; @@ -56,6 +57,7 @@ describe('state', function () { .state('HHH', HHH) .state('OPT', OPT) .state('OPT.OPT2', OPT2) + .state('URLLESS', URLLESS) .state('ISS2101', ISS2101) .state('RS', RS) .state('RSP', RSP) @@ -989,6 +991,7 @@ describe('state', function () { 'OPT.OPT2', 'RS', 'RSP', + 'URLLESS', 'about', 'about.person', 'about.person.item', @@ -1255,6 +1258,34 @@ describe('state', function () { extend(params, { p5: true }); check('types.substate', "/types/foo/2014-11-15/sub/10/%7B%22baz%22:%22qux%22%7D?p5=1", params, defaults, nonurl); })); + + it('should support non-url parameters', inject(function($state, $q, $stateParams) { + $state.transitionTo(A); $q.flush(); + expect($state.is(A)).toBe(true); + + $state.go('URLLESS', { myparam: "0"}); $q.flush(); // string "0" decodes to 0 + expect($state.current.name).toBe("URLLESS"); + expect($stateParams.myparam).toBe(0); + + $state.go('URLLESS', { myparam: "1"}); $q.flush(); // string "1" decodes to 1 + expect($stateParams.myparam).toBe(1); + })); + + it('should not transition if a required non-url parameter is missing', inject(function($state, $q, $stateParams) { + $state.transitionTo(A); $q.flush(); + expect($state.current.name).toBe("A"); + + $state.go('URLLESS'); $q.flush(); // Missing required parameter; transition fails + expect($state.current.name).toBe("A"); + })); + + it('should not transition if a required non-url parameter is invalid', inject(function($state, $q, $stateParams) { + $state.transitionTo(A); $q.flush(); + expect($state.current.name).toBe("A"); + + $state.go('URLLESS', { myparam: "somestring"}); $q.flush(); // string "somestring" is not an int + expect($state.current.name).toBe("A"); + })); }); it('should revert to last known working url on state change failure', inject(function ($state, $rootScope, $location, $q) { diff --git a/test/urlMatcherFactorySpec.js b/test/urlMatcherFactorySpec.js index 3d6cf15d4..38b694ac4 100755 --- a/test/urlMatcherFactorySpec.js +++ b/test/urlMatcherFactorySpec.js @@ -520,6 +520,27 @@ describe("urlMatcherFactory", function () { expect(m.format({ foo: 5, flag: true })).toBe("/5/1"); }); + it("should match types named only in params", function () { + var m = new UrlMatcher("/{foo}/{flag}", { + params: { + foo: { type: 'int'}, + flag: { type: 'bool'} + } + }); + expect(m.exec("/1138/1")).toEqual({foo: 1138, flag: true}); + expect(m.format({foo: 5, flag: true})).toBe("/5/1"); + }); + + it("should throw an error if a param type is declared twice", function () { + expect(function() { + new UrlMatcher("/{foo:int}", { + params: { + foo: {type: 'int'} + } + }); + }).toThrow("Param 'foo' has two type configurations."); + }); + it("should encode/decode dates", function () { var m = new UrlMatcher("/calendar/{date:date}"), result = m.exec("/calendar/2014-03-26");