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

fix(input): correctly handle invalid model values for `input[date/time/…... #9375

Merged
merged 1 commit into from
Oct 1, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/content/error/ngModel/datefmt.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@ngdoc error
@name ngModel:datefmt
@fullName Model is not a date object
@description

All date-related inputs like `<input type="date">` 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 `<input>` 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.

10 changes: 9 additions & 1 deletion src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
Expand All @@ -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;
Expand Down
67 changes: 40 additions & 27 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<input type="month" ng-model="january"/>');

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 (){
Expand Down Expand Up @@ -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('<input type="week" ng-model="secondWeek"/>');

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 (){
Expand Down Expand Up @@ -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('<input type="datetime-local" ng-model="lunchtime"/>');

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(){
Expand Down Expand Up @@ -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('<input type="time" ng-model="lunchtime"/>');

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(){
Expand Down Expand Up @@ -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('<input type="date" ng-model="birthday"/>');

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('<input type="date" ng-model="val"/>');
// 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('');
Expand Down