Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 088545c

Browse files
committed
fix(ngModel): properly parse min/max date values as strings for date inputs
Due to the nature of how date objects are rendered when JSON.stringify is called, the resulting string contains two sets of quotes surrounding it. This commit fixes that issue. Closes #6755
1 parent b350283 commit 088545c

File tree

2 files changed

+83
-22
lines changed

2 files changed

+83
-22
lines changed

src/ng/directive/input.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
TOUCHED_CLASS: true,
99
*/
1010

11+
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
12+
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
1113
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
1214
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
1315
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
@@ -1036,6 +1038,15 @@ function createDateParser(regexp, mapping) {
10361038
}
10371039

10381040
if (isString(iso)) {
1041+
// When a date is JSON'ified to wraps itself inside of an extra
1042+
// set of double quotes. This makes the date parsing code unable
1043+
// to match the date string and parse it as a date.
1044+
if (iso.charAt(0) == '"' && iso.charAt(iso.length-1) == '"') {
1045+
iso = iso.substring(1, iso.length-1);
1046+
}
1047+
if (ISO_DATE_REGEXP.test(iso)) {
1048+
return new Date(iso);
1049+
}
10391050
regexp.lastIndex = 0;
10401051
parts = regexp.exec(iso);
10411052

@@ -1082,7 +1093,7 @@ function createDateInputType(type, regexp, parseDate, format) {
10821093
return '';
10831094
});
10841095

1085-
if (attr.min || attr.ngMin) {
1096+
if (isDefined(attr.min) || attr.ngMin) {
10861097
var minVal;
10871098
ctrl.$validators.min = function(value) {
10881099
return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal;
@@ -1093,7 +1104,7 @@ function createDateInputType(type, regexp, parseDate, format) {
10931104
});
10941105
}
10951106

1096-
if (attr.max || attr.ngMax) {
1107+
if (isDefined(attr.max) || attr.ngMax) {
10971108
var maxVal;
10981109
ctrl.$validators.max = function(value) {
10991110
return ctrl.$isEmpty(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;

test/ng/directive/inputSpec.js

+70-20
Original file line numberDiff line numberDiff line change
@@ -2491,32 +2491,40 @@ describe('input', function() {
24912491
});
24922492
});
24932493

2494-
it('should validate even if max value changes on-the-fly', function(done) {
2494+
it('should validate even if max value changes on-the-fly', function() {
24952495
scope.max = '2013-01-01T01:02:00';
24962496
compileInput('<input type="datetime-local" ng-model="value" name="alias" max="{{max}}" />');
24972497

24982498
changeInputValueTo('2014-01-01T12:34:00');
24992499
expect(inputElm).toBeInvalid();
25002500

25012501
scope.max = '2001-01-01T01:02:00';
2502-
scope.$digest(function () {
2503-
expect(inputElm).toBeValid();
2504-
done();
2505-
});
2502+
scope.$digest();
2503+
2504+
expect(inputElm).toBeInvalid();
2505+
2506+
scope.max = '2024-01-01T01:02:00';
2507+
scope.$digest();
2508+
2509+
expect(inputElm).toBeValid();
25062510
});
25072511

2508-
it('should validate even if min value changes on-the-fly', function(done) {
2512+
it('should validate even if min value changes on-the-fly', function() {
25092513
scope.min = '2013-01-01T01:02:00';
25102514
compileInput('<input type="datetime-local" ng-model="value" name="alias" min="{{min}}" />');
25112515

25122516
changeInputValueTo('2010-01-01T12:34:00');
25132517
expect(inputElm).toBeInvalid();
25142518

25152519
scope.min = '2014-01-01T01:02:00';
2516-
scope.$digest(function () {
2517-
expect(inputElm).toBeValid();
2518-
done();
2519-
});
2520+
scope.$digest();
2521+
2522+
expect(inputElm).toBeInvalid();
2523+
2524+
scope.min = '2009-01-01T01:02:00';
2525+
scope.$digest();
2526+
2527+
expect(inputElm).toBeValid();
25202528
});
25212529
});
25222530

@@ -2837,6 +2845,23 @@ describe('input', function() {
28372845
expect(+scope.value).toBe(+new Date(2000, 0, 1));
28382846
expect(scope.form.alias.$error.min).toBeFalsy();
28392847
});
2848+
2849+
it('should parse ISO-based date strings as a valid min date value', inject(function($rootScope) {
2850+
var scope = $rootScope.$new();
2851+
var element = $compile('<form name="myForm">' +
2852+
'<input name="myControl" type="date" min="{{ min }}" ng-model="value">' +
2853+
'</form>')(scope);
2854+
2855+
var inputElm = element.find('input');
2856+
2857+
scope.value = new Date(2010, 1, 1, 0, 0, 0);
2858+
scope.min = new Date(2014, 10, 10, 0, 0, 0);
2859+
scope.$digest();
2860+
2861+
expect(scope.myForm.myControl.$error.min).toBeTruthy();
2862+
2863+
dealoc(element);
2864+
}));
28402865
});
28412866

28422867
describe('max', function (){
@@ -2857,34 +2882,59 @@ describe('input', function() {
28572882
expect(+scope.value).toBe(+new Date(2000, 0, 1));
28582883
expect(scope.form.alias.$error.max).toBeFalsy();
28592884
});
2885+
2886+
it('should parse ISO-based date strings as a valid max date value', inject(function($rootScope) {
2887+
var scope = $rootScope.$new();
2888+
var element = $compile('<form name="myForm">' +
2889+
'<input name="myControl" type="date" max="{{ max }}" ng-model="value">' +
2890+
'</form>')(scope);
2891+
2892+
var inputElm = element.find('input');
2893+
2894+
scope.value = new Date(2020, 1, 1, 0, 0, 0);
2895+
scope.max = new Date(2014, 10, 10, 0, 0, 0);
2896+
scope.$digest();
2897+
2898+
expect(scope.myForm.myControl.$error.max).toBeTruthy();
2899+
2900+
dealoc(element);
2901+
}));
28602902
});
28612903

2862-
it('should validate even if max value changes on-the-fly', function(done) {
2904+
it('should validate even if max value changes on-the-fly', function() {
28632905
scope.max = '2013-01-01';
28642906
compileInput('<input type="date" ng-model="value" name="alias" max="{{max}}" />');
28652907

28662908
changeInputValueTo('2014-01-01');
28672909
expect(inputElm).toBeInvalid();
28682910

28692911
scope.max = '2001-01-01';
2870-
scope.$digest(function () {
2871-
expect(inputElm).toBeValid();
2872-
done();
2873-
});
2912+
scope.$digest();
2913+
2914+
expect(inputElm).toBeInvalid();
2915+
2916+
scope.max = '2021-01-01';
2917+
scope.$digest();
2918+
2919+
expect(inputElm).toBeValid();
28742920
});
28752921

2876-
it('should validate even if min value changes on-the-fly', function(done) {
2922+
it('should validate even if min value changes on-the-fly', function() {
28772923
scope.min = '2013-01-01';
28782924
compileInput('<input type="date" ng-model="value" name="alias" min="{{min}}" />');
28792925

28802926
changeInputValueTo('2010-01-01');
28812927
expect(inputElm).toBeInvalid();
28822928

28832929
scope.min = '2014-01-01';
2884-
scope.$digest(function () {
2885-
expect(inputElm).toBeValid();
2886-
done();
2887-
});
2930+
scope.$digest();
2931+
2932+
expect(inputElm).toBeInvalid();
2933+
2934+
scope.min = '2009-01-01';
2935+
scope.$digest();
2936+
2937+
expect(inputElm).toBeValid();
28882938
});
28892939
});
28902940

0 commit comments

Comments
 (0)