From 5c5da98a7eb0df2f97c6ea8a397e51be7796479c Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Tue, 8 Sep 2015 15:15:52 +0300 Subject: [PATCH 1/3] fix(input): ignore min/max if they are empty on all input types When the min/max attributes are empty (i.e. `attrs.min/max === ''`), there should be no min/max validation applied (i.e. all values should be valid wrt min/max). This works correctly for `input[number]`, but not for date input family (`input[date/datetime-local/time/week/month]`). In the case on `input[number]`, an empty string for `attrs.min/max` is translated to `undefined` for `minVal/maxVal` and a check for `isUndefined(minVal/maxVal)` ensures that no min/max validation takes place. For the data input family, an empty string for `attrs.min/max` is translated to `NaN` (by `parseDate()`), so an additional check (for `isNaN(minVal/maxVal)`) is required. Fixes #12363 --- src/ng/directive/input.js | 4 +- test/ng/directive/inputSpec.js | 91 ++++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 0403d6cf35b2..60c804ff7356 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1301,7 +1301,7 @@ function createDateInputType(type, regexp, parseDate, format) { if (isDefined(attr.min) || attr.ngMin) { var minVal; ctrl.$validators.min = function(value) { - return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; + return !isValidDate(value) || isUndefined(minVal) || isNaN(minVal) || parseDate(value) >= minVal; }; attr.$observe('min', function(val) { minVal = parseObservedDateValue(val); @@ -1312,7 +1312,7 @@ function createDateInputType(type, regexp, parseDate, format) { if (isDefined(attr.max) || attr.ngMax) { var maxVal; ctrl.$validators.max = function(value) { - return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; + return !isValidDate(value) || isUndefined(maxVal) || isNaN(maxVal) || parseDate(value) <= maxVal; }; attr.$observe('max', function(val) { maxVal = parseObservedDateValue(val); diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 3c92625a1b43..95763b1d2bdb 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -688,6 +688,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); }); describe('max', function() { @@ -722,6 +730,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); }); }); @@ -886,6 +902,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); }); describe('max', function() { @@ -921,6 +945,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); }); }); @@ -1119,6 +1151,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); }); describe('max', function() { @@ -1153,6 +1193,14 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); }); @@ -1428,12 +1476,21 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); }); describe('max', function() { var inputElm; beforeEach(function() { - inputElm = helper.compileInput(''); + $rootScope.maxVal = '22:30:00'; + inputElm = helper.compileInput(''); }); it('should invalidate', function() { @@ -1449,11 +1506,19 @@ describe('input', function() { expect(+$rootScope.value).toBe(+new Date(1970, 0, 1, 5, 30, 0)); expect($rootScope.form.alias.$error.max).toBeFalsy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); }); it('should validate even if max value changes on-the-fly', function() { - $rootScope.max = '4:02:00'; + $rootScope.max = '04:02:00'; var inputElm = helper.compileInput(''); helper.changeInputValueTo('05:34:00'); @@ -1481,7 +1546,7 @@ describe('input', function() { it('should validate even if ng-max value changes on-the-fly', function() { - $rootScope.max = '4:02:00'; + $rootScope.max = '04:02:00'; var inputElm = helper.compileInput(''); helper.changeInputValueTo('05:34:00'); @@ -1706,6 +1771,16 @@ describe('input', function() { expect($rootScope.form.myControl.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + var inputElm = helper.compileInput( + ''); + + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); }); describe('max', function() { @@ -1735,6 +1810,16 @@ describe('input', function() { expect($rootScope.form.myControl.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + var inputElm = helper.compileInput( + ''); + + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); }); From c00a93bff502fd440acabed1f74b5346a4b299d9 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Tue, 8 Sep 2015 19:38:32 +0300 Subject: [PATCH 2/3] perf(input): convert `NaN` to `undefined` to avoid an extra check in validators --- src/ng/directive/input.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 60c804ff7356..649477d2995f 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1301,7 +1301,7 @@ function createDateInputType(type, regexp, parseDate, format) { if (isDefined(attr.min) || attr.ngMin) { var minVal; ctrl.$validators.min = function(value) { - return !isValidDate(value) || isUndefined(minVal) || isNaN(minVal) || parseDate(value) >= minVal; + return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; }; attr.$observe('min', function(val) { minVal = parseObservedDateValue(val); @@ -1312,7 +1312,7 @@ function createDateInputType(type, regexp, parseDate, format) { if (isDefined(attr.max) || attr.ngMax) { var maxVal; ctrl.$validators.max = function(value) { - return !isValidDate(value) || isUndefined(maxVal) || isNaN(maxVal) || parseDate(value) <= maxVal; + return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; }; attr.$observe('max', function(val) { maxVal = parseObservedDateValue(val); @@ -1326,7 +1326,7 @@ function createDateInputType(type, regexp, parseDate, format) { } function parseObservedDateValue(val) { - return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; + return (isDefined(val) && !isDate(val)) ? (parseDate(val) || undefined) : val; } }; } From b0b11ef6857024051d9d074cd089c45edaf2438c Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Wed, 9 Sep 2015 04:15:35 +0300 Subject: [PATCH 3/3] style(input): remove redundant parenthesis --- src/ng/directive/input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 649477d2995f..4b0d8cf4b1c3 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1326,7 +1326,7 @@ function createDateInputType(type, regexp, parseDate, format) { } function parseObservedDateValue(val) { - return (isDefined(val) && !isDate(val)) ? (parseDate(val) || undefined) : val; + return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val; } }; }