Permalink
Comparing changes
Open a pull request
- 3 commits
- 3 files changed
- 0 commit comments
- 1 contributor
Commits on Nov 24, 2014
- Type.$normalize can be passed either a decoded object, or an encoded object (string). If the parameter is already decoded (checked via .is()), then it is returned. Else the parameter is decoded and returned.
Unified
Split
Showing
with
42 additions
and 20 deletions.
- +5 −1 Gruntfile.js
- +29 −13 src/urlMatcherFactory.js
- +8 −6 test/urlMatcherFactorySpec.js
| @@ -140,7 +140,11 @@ module.exports = function (grunt) { | ||
|
|
||
| grunt.registerTask('widedocs', 'Convert to bootstrap container-fluid', function () { | ||
| promising(this, | ||
| system('sed -i.bak -e \'s/class="row"/class="row-fluid"/\' -e \'s/role="main" class="container"/role="main" class="container-fluid"/\' site/index.html') | ||
| system( | ||
| 'sed -i.bak ' + | ||
| '-e \'s/class="row"/class="row-fluid"/\' ' + | ||
| '-e \'s/icon-cog"><\\/i>/icon-cog"><\\/i>Provider/\' ' + | ||
| '-e \'s/role="main" class="container"/role="main" class="container-fluid"/\' site/index.html') | ||
| ); | ||
| }); | ||
|
|
||
| @@ -480,6 +480,11 @@ Type.prototype.pattern = /.*/; | ||
|
|
||
| Type.prototype.toString = function() { return "{Type:" + this.name + "}"; }; | ||
|
|
||
| /** Given an encoded string, or a decoded object, returns a decoded object */ | ||
| Type.prototype.$normalize = function(val) { | ||
| return this.is(val) ? val : this.decode(val); | ||
| }; | ||
|
|
||
| /* | ||
| * Wraps an existing custom Type as an array of Type, depending on 'mode'. | ||
| * e.g.: | ||
| @@ -493,7 +498,6 @@ Type.prototype.toString = function() { return "{Type:" + this.name + "}"; }; | ||
| Type.prototype.$asArray = function(mode, isSearch) { | ||
| if (!mode) return this; | ||
| if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only"); | ||
| return new ArrayType(this, mode); | ||
|
|
||
| function ArrayType(type, mode) { | ||
| function bindTo(type, callbackName) { | ||
| @@ -542,8 +546,12 @@ Type.prototype.$asArray = function(mode, isSearch) { | ||
| this.is = arrayHandler(bindTo(type, 'is'), true); | ||
| this.equals = arrayEqualsHandler(bindTo(type, 'equals')); | ||
| this.pattern = type.pattern; | ||
| this.$normalize = arrayHandler(bindTo(type, '$normalize')); | ||
| this.name = type.name; | ||
| this.$arrayMode = mode; | ||
| } | ||
|
|
||
| return new ArrayType(this, mode); | ||
| }; | ||
|
|
||
|
|
||
| @@ -571,7 +579,7 @@ function $UrlMatcherFactory() { | ||
| string: { | ||
| encode: valToString, | ||
| decode: valFromString, | ||
| is: regexpMatches, | ||
| is: function(val) { return typeof val === "string"}, | ||
| pattern: /[^/]*/ | ||
| }, | ||
| int: { | ||
| @@ -945,7 +953,10 @@ function $UrlMatcherFactory() { | ||
| */ | ||
| function $$getDefaultValue() { | ||
| if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); | ||
| return injector.invoke(config.$$fn); | ||
| var defaultValue = injector.invoke(config.$$fn); | ||
| if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue)) | ||
| throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")"); | ||
| return defaultValue; | ||
| } | ||
|
|
||
| /** | ||
| @@ -959,7 +970,7 @@ function $UrlMatcherFactory() { | ||
| return replacement.length ? replacement[0] : value; | ||
| } | ||
| value = $replace(value); | ||
| return isDefined(value) ? self.type.decode(value) : $$getDefaultValue(); | ||
| return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value); | ||
| } | ||
|
|
||
| function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; } | ||
| @@ -1015,15 +1026,20 @@ function $UrlMatcherFactory() { | ||
| return equal; | ||
| }, | ||
| $$validates: function $$validate(paramValues) { | ||
| var result = true, isOptional, val, param, self = this; | ||
|
|
||
| forEach(this.$$keys(), function(key) { | ||
| param = self[key]; | ||
| val = paramValues[key]; | ||
| isOptional = !val && param.isOptional; | ||
| result = result && (isOptional || !!param.type.is(val)); | ||
| }); | ||
| return result; | ||
| var keys = this.$$keys(), i, param, rawVal, normalized, encoded; | ||
| for (i = 0; i < keys.length; i++) { | ||
| param = this[keys[i]]; | ||
| rawVal = paramValues[keys[i]]; | ||
| if ((rawVal === undefined || rawVal === null) && param.isOptional) | ||
| break; // There was no parameter value, but the param is optional | ||
| normalized = param.type.$normalize(rawVal); | ||
| if (!param.type.is(normalized)) | ||
| return false; // The value was not of the correct Type, and could not be decoded to the correct Type | ||
| encoded = param.type.encode(normalized); | ||
| if (angular.isString(encoded) && !param.type.pattern.exec(encoded)) | ||
| return false; // The value was of the correct type, but when encoded, did not match the Type's regexp | ||
| } | ||
| return true; | ||
| }, | ||
| $$parent: undefined | ||
| }; | ||
| @@ -407,15 +407,17 @@ describe("urlMatcherFactoryProvider", function () { | ||
| var m; | ||
| beforeEach(module('ui.router.util', function($urlMatcherFactoryProvider) { | ||
| $urlMatcherFactoryProvider.type("myType", {}, function() { | ||
| return { decode: function() { return 'decoded'; } | ||
| }; | ||
| return { | ||
| decode: function() { return { status: 'decoded' }; }, | ||
| is: angular.isObject | ||
| }; | ||
| }); | ||
| m = new UrlMatcher("/test?{foo:myType}"); | ||
| })); | ||
|
|
||
| it("should handle arrays properly with config-time custom type definitions", inject(function ($stateParams) { | ||
| expect(m.exec("/test", {foo: '1'})).toEqual({ foo: 'decoded' }); | ||
| expect(m.exec("/test", {foo: ['1', '2']})).toEqual({ foo: ['decoded', 'decoded'] }); | ||
| expect(m.exec("/test", {foo: '1'})).toEqual({ foo: { status: 'decoded' } }); | ||
| expect(m.exec("/test", {foo: ['1', '2']})).toEqual({ foo: [ { status: 'decoded' }, { status: 'decoded' }] }); | ||
| })); | ||
| }); | ||
| }); | ||
| @@ -662,7 +664,7 @@ describe("urlMatcherFactory", function () { | ||
|
|
||
| it("should populate query params", function() { | ||
| var defaults = { order: "name", limit: 25, page: 1 }; | ||
| var m = new UrlMatcher('/foo?order&limit&page', { | ||
| var m = new UrlMatcher('/foo?order&{limit:int}&{page:int}', { | ||
| params: defaults | ||
| }); | ||
| expect(m.exec("/foo")).toEqual(defaults); | ||
| @@ -687,7 +689,7 @@ describe("urlMatcherFactory", function () { | ||
| }); | ||
|
|
||
| it("should allow injectable functions", inject(function($stateParams) { | ||
| var m = new UrlMatcher('/users/:user', { | ||
| var m = new UrlMatcher('/users/{user:json}', { | ||
| params: { | ||
| user: function($stateParams) { | ||
| return $stateParams.user; | ||