mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-29 13:25:40 +08:00
fix(input): correctly handle invalid model values for input[date/time/…]
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
This commit is contained in:
11
docs/content/error/ngModel/datefmt.ngdoc
Normal file
11
docs/content/error/ngModel/datefmt.ngdoc
Normal file
@@ -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.
|
||||||
|
|
||||||
@@ -1111,7 +1111,10 @@ function createDateInputType(type, regexp, parseDate, format) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ctrl.$formatters.push(function(value) {
|
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 $filter('date')(value, format, timezone);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
@@ -1138,6 +1141,11 @@ function createDateInputType(type, regexp, parseDate, format) {
|
|||||||
ctrl.$validate();
|
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) {
|
function parseObservedDateValue(val) {
|
||||||
return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
|
return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
|
||||||
|
|||||||
@@ -2251,14 +2251,14 @@ describe('input', function() {
|
|||||||
|
|
||||||
// INPUT TYPES
|
// INPUT TYPES
|
||||||
describe('month', function (){
|
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"/>');
|
compileInput('<input type="month" ng-model="january"/>');
|
||||||
|
|
||||||
scope.$apply(function(){
|
expect(function() {
|
||||||
scope.january = '2013-01';
|
scope.$apply(function(){
|
||||||
});
|
scope.january = '2013-01';
|
||||||
|
});
|
||||||
expect(inputElm.val()).toBe('');
|
}).toThrowMinErr('ngModel', 'datefmt', 'Expected `2013-01` to be a date');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the view if the model is a valid Date object', function (){
|
it('should set the view if the model is a valid Date object', function (){
|
||||||
@@ -2433,14 +2433,14 @@ describe('input', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('week', 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"/>');
|
compileInput('<input type="week" ng-model="secondWeek"/>');
|
||||||
|
|
||||||
scope.$apply(function(){
|
expect(function() {
|
||||||
scope.secondWeek = '2013-W02';
|
scope.$apply(function(){
|
||||||
});
|
scope.secondWeek = '2013-W02';
|
||||||
|
});
|
||||||
expect(inputElm.val()).toBe('');
|
}).toThrowMinErr('ngModel', 'datefmt', 'Expected `2013-W02` to be a date');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the view if the model is a valid Date object', function (){
|
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 () {
|
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"/>');
|
compileInput('<input type="datetime-local" ng-model="lunchtime"/>');
|
||||||
|
|
||||||
scope.$apply(function(){
|
expect(function() {
|
||||||
scope.lunchtime = '2013-12-16T11:30:00';
|
scope.$apply(function(){
|
||||||
});
|
scope.lunchtime = '2013-12-16T11:30:00';
|
||||||
|
});
|
||||||
expect(inputElm.val()).toBe('');
|
}).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(){
|
it('should set the view if the model if a valid Date object.', function(){
|
||||||
@@ -2890,14 +2890,14 @@ describe('input', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('time', 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"/>');
|
compileInput('<input type="time" ng-model="lunchtime"/>');
|
||||||
|
|
||||||
scope.$apply(function(){
|
expect(function() {
|
||||||
scope.lunchtime = '11:30:00';
|
scope.$apply(function(){
|
||||||
});
|
scope.lunchtime = '11:30:00';
|
||||||
|
});
|
||||||
expect(inputElm.val()).toBe('');
|
}).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(){
|
it('should set the view if the model if a valid Date object.', function(){
|
||||||
@@ -3141,11 +3141,24 @@ describe('input', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('date', 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"/>');
|
compileInput('<input type="date" ng-model="birthday"/>');
|
||||||
|
|
||||||
scope.$apply(function(){
|
expect(function() {
|
||||||
scope.birthday = '1977-10-22';
|
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('');
|
expect(inputElm.val()).toBe('');
|
||||||
|
|||||||
Reference in New Issue
Block a user