Skip to content

Commit a17a1d5

Browse files
committed
fix(ngModel): properly parse min/max date values as strings
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 angular#6755
1 parent 4102ff1 commit a17a1d5

File tree

2 files changed

+82
-22
lines changed

2 files changed

+82
-22
lines changed

src/ng/directive/input.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
1212
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;
1313
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
14+
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)/;
1415
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
1516
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d))?$/;
1617
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
@@ -1036,6 +1037,15 @@ function createDateParser(regexp, mapping) {
10361037
}
10371038

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

@@ -1082,7 +1092,7 @@ function createDateInputType(type, regexp, parseDate, format) {
10821092
return '';
10831093
});
10841094

1085-
if (attr.min || attr.ngMin) {
1095+
if (isDefined(attr.min) || attr.ngMin) {
10861096
var minVal;
10871097
ctrl.$validators.min = function(value) {
10881098
return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal;
@@ -1093,7 +1103,7 @@ function createDateInputType(type, regexp, parseDate, format) {
10931103
});
10941104
}
10951105

1096-
if (attr.max || attr.ngMax) {
1106+
if (isDefined(attr.max) || attr.ngMax) {
10971107
var maxVal;
10981108
ctrl.$validators.max = function(value) {
10991109
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)