Skip to content

Commit e401024

Browse files
fix(urlMatcherFactory): fix configuring a parameter type by name in a params block
closes #2294
1 parent 1c763c8 commit e401024

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

src/urlMatcherFactory.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ function UrlMatcher(pattern, config, parentMatcher) {
122122
cfg = config.params[id];
123123
segment = pattern.substring(last, m.index);
124124
regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
125-
type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
125+
126+
if (regexp) {
127+
type = $$UMFP.type(regexp) || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
128+
}
129+
126130
return {
127131
id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
128132
};
@@ -920,7 +924,12 @@ function $UrlMatcherFactory() {
920924
if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
921925
if (urlType) return urlType;
922926
if (!config.type) return (location === "config" ? $types.any : $types.string);
923-
return config.type instanceof Type ? config.type : new Type(config.type);
927+
928+
if (angular.isString(config.type))
929+
return $types[config.type];
930+
if (config.type instanceof Type)
931+
return config.type;
932+
return new Type(config.type);
924933
}
925934

926935
// array config: param name (param[]) overrides default settings. explicit config overrides param name.

test/stateSpec.js

+31
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ describe('state', function () {
3232
RSP = { url: '^/:doReload/search?term', reloadOnSearch: false },
3333
OPT = { url: '/opt/:param', params: { param: "100" } },
3434
OPT2 = { url: '/opt2/:param2/:param3', params: { param3: "300", param4: "400" } },
35+
URLLESS = { url: '/urllessparams', params: { myparam: { type: 'int' } } },
3536
ISS2101 = { params: { bar: { squash: false, value: 'qux'}}, url: '/2101/{bar:string}' };
3637
AppInjectable = {};
3738

@@ -56,6 +57,7 @@ describe('state', function () {
5657
.state('HHH', HHH)
5758
.state('OPT', OPT)
5859
.state('OPT.OPT2', OPT2)
60+
.state('URLLESS', URLLESS)
5961
.state('ISS2101', ISS2101)
6062
.state('RS', RS)
6163
.state('RSP', RSP)
@@ -989,6 +991,7 @@ describe('state', function () {
989991
'OPT.OPT2',
990992
'RS',
991993
'RSP',
994+
'URLLESS',
992995
'about',
993996
'about.person',
994997
'about.person.item',
@@ -1255,6 +1258,34 @@ describe('state', function () {
12551258
extend(params, { p5: true });
12561259
check('types.substate', "/types/foo/2014-11-15/sub/10/%7B%22baz%22:%22qux%22%7D?p5=1", params, defaults, nonurl);
12571260
}));
1261+
1262+
it('should support non-url parameters', inject(function($state, $q, $stateParams) {
1263+
$state.transitionTo(A); $q.flush();
1264+
expect($state.is(A)).toBe(true);
1265+
1266+
$state.go('URLLESS', { myparam: "0"}); $q.flush(); // string "0" decodes to 0
1267+
expect($state.current.name).toBe("URLLESS");
1268+
expect($stateParams.myparam).toBe(0);
1269+
1270+
$state.go('URLLESS', { myparam: "1"}); $q.flush(); // string "1" decodes to 1
1271+
expect($stateParams.myparam).toBe(1);
1272+
}));
1273+
1274+
it('should not transition if a required non-url parameter is missing', inject(function($state, $q, $stateParams) {
1275+
$state.transitionTo(A); $q.flush();
1276+
expect($state.current.name).toBe("A");
1277+
1278+
$state.go('URLLESS'); $q.flush(); // Missing required parameter; transition fails
1279+
expect($state.current.name).toBe("A");
1280+
}));
1281+
1282+
it('should not transition if a required non-url parameter is invalid', inject(function($state, $q, $stateParams) {
1283+
$state.transitionTo(A); $q.flush();
1284+
expect($state.current.name).toBe("A");
1285+
1286+
$state.go('URLLESS', { myparam: "somestring"}); $q.flush(); // string "somestring" is not an int
1287+
expect($state.current.name).toBe("A");
1288+
}));
12581289
});
12591290

12601291
it('should revert to last known working url on state change failure', inject(function ($state, $rootScope, $location, $q) {

test/urlMatcherFactorySpec.js

+21
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,27 @@ describe("urlMatcherFactory", function () {
520520
expect(m.format({ foo: 5, flag: true })).toBe("/5/1");
521521
});
522522

523+
it("should match types named only in params", function () {
524+
var m = new UrlMatcher("/{foo}/{flag}", {
525+
params: {
526+
foo: { type: 'int'},
527+
flag: { type: 'bool'}
528+
}
529+
});
530+
expect(m.exec("/1138/1")).toEqual({foo: 1138, flag: true});
531+
expect(m.format({foo: 5, flag: true})).toBe("/5/1");
532+
});
533+
534+
it("should throw an error if a param type is declared twice", function () {
535+
expect(function() {
536+
new UrlMatcher("/{foo:int}", {
537+
params: {
538+
foo: {type: 'int'}
539+
}
540+
});
541+
}).toThrow("Param 'foo' has two type configurations.");
542+
});
543+
523544
it("should encode/decode dates", function () {
524545
var m = new UrlMatcher("/calendar/{date:date}"),
525546
result = m.exec("/calendar/2014-03-26");

0 commit comments

Comments
 (0)