From a0bfdd0d60882125f614a91c321f12f730735e7b Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 1 Oct 2014 15:24:44 -0700 Subject: [PATCH] =?UTF-8?q?fix(input):=20correctly=20handle=20invalid=20mo?= =?UTF-8?q?del=20values=20for=20`input[date/time/=E2=80=A6]`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to `input[number]` Angular will throw if the model value for a `input[date]` is not a `Date` object. For `Invalid Date`s (dates whose `getTime()` is `NaN`) `input[date]` will render an empty string. Closes #8949 Closes #9375 --- docs/content/error/ngModel/datefmt.ngdoc | 11 ++++ src/ng/directive/input.js | 10 +++- test/ng/directive/inputSpec.js | 67 ++++++++++++++---------- 3 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 docs/content/error/ngModel/datefmt.ngdoc diff --git a/docs/content/error/ngModel/datefmt.ngdoc b/docs/content/error/ngModel/datefmt.ngdoc new file mode 100644 index 000000000000..57c2fce1d649 --- /dev/null +++ b/docs/content/error/ngModel/datefmt.ngdoc @@ -0,0 +1,11 @@ +@ngdoc error +@name ngModel:datefmt +@fullName Model is not a date object +@description + +All date-related inputs like `` require the model to be a `Date` object. +If the model is something else, this error will be thrown. +Angular does not set validation errors on the `` in this case +as those errors are shown to the user, but the erroneous state was +caused by incorrect application logic and not by the user. + diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index ae24ce8f5deb..2a89d745d3c6 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1111,7 +1111,10 @@ function createDateInputType(type, regexp, parseDate, format) { }); ctrl.$formatters.push(function(value) { - if (isDate(value)) { + if (!ctrl.$isEmpty(value)) { + if (!isDate(value)) { + throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + } return $filter('date')(value, format, timezone); } return ''; @@ -1138,6 +1141,11 @@ function createDateInputType(type, regexp, parseDate, format) { ctrl.$validate(); }); } + // Override the standard $isEmpty to detect invalid dates as well + ctrl.$isEmpty = function(value) { + // Invalid Date: getTime() returns NaN + return !value || (value.getTime && value.getTime() !== value.getTime()); + }; function parseObservedDateValue(val) { return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index a10676732a14..9e075c0706ea 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -2251,14 +2251,14 @@ describe('input', function() { // INPUT TYPES describe('month', function (){ - it('should render blank if model is not a Date object', function() { + it('should throw if model is not a Date object', function() { compileInput(''); - scope.$apply(function(){ - scope.january = '2013-01'; - }); - - expect(inputElm.val()).toBe(''); + expect(function() { + scope.$apply(function(){ + scope.january = '2013-01'; + }); + }).toThrowMinErr('ngModel', 'datefmt', 'Expected `2013-01` to be a date'); }); it('should set the view if the model is a valid Date object', function (){ @@ -2433,14 +2433,14 @@ describe('input', function() { }); describe('week', function (){ - it('should set render blank if model is not a Date object', function() { + it('should throw if model is not a Date object', function() { compileInput(''); - scope.$apply(function(){ - scope.secondWeek = '2013-W02'; - }); - - expect(inputElm.val()).toBe(''); + expect(function() { + scope.$apply(function(){ + scope.secondWeek = '2013-W02'; + }); + }).toThrowMinErr('ngModel', 'datefmt', 'Expected `2013-W02` to be a date'); }); it('should set the view if the model is a valid Date object', function (){ @@ -2615,14 +2615,14 @@ describe('input', function() { }); describe('datetime-local', function () { - it('should render blank if model is not a Date object', function() { + it('should throw if model is not a Date object', function() { compileInput(''); - scope.$apply(function(){ - scope.lunchtime = '2013-12-16T11:30:00'; - }); - - expect(inputElm.val()).toBe(''); + expect(function() { + scope.$apply(function(){ + scope.lunchtime = '2013-12-16T11:30:00'; + }); + }).toThrowMinErr('ngModel', 'datefmt', 'Expected `2013-12-16T11:30:00` to be a date'); }); it('should set the view if the model if a valid Date object.', function(){ @@ -2890,14 +2890,14 @@ describe('input', function() { }); describe('time', function () { - it('should render blank if model is not a Date object', function() { + it('should throw if model is not a Date object', function() { compileInput(''); - scope.$apply(function(){ - scope.lunchtime = '11:30:00'; - }); - - expect(inputElm.val()).toBe(''); + expect(function() { + scope.$apply(function(){ + scope.lunchtime = '11:30:00'; + }); + }).toThrowMinErr('ngModel', 'datefmt', 'Expected `11:30:00` to be a date'); }); it('should set the view if the model if a valid Date object.', function(){ @@ -3141,11 +3141,24 @@ describe('input', function() { }); describe('date', function () { - it('should render blank if model is not a Date object.', function() { + it('should throw if model is not a Date object.', function() { compileInput(''); - scope.$apply(function(){ - scope.birthday = '1977-10-22'; + expect(function() { + scope.$apply(function(){ + scope.birthday = '1977-10-22'; + }); + }).toThrowMinErr('ngModel', 'datefmt', 'Expected `1977-10-22` to be a date'); + }); + + it('should set the view to empty when the model is an InvalidDate', function() { + compileInput(''); + // reset the element type to text otherwise newer browsers + // would always set the input.value to empty for invalid dates... + inputElm.attr('type', 'text'); + + scope.$apply(function (){ + scope.val = new Date('a'); }); expect(inputElm.val()).toBe('');