Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.
Closed
63 changes: 62 additions & 1 deletion src/datepicker/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,10 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
currentText: '@',
clearText: '@',
closeText: '@',
dateDisabled: '&'
dateDisabled: '&',
minDate: '@',
maxDate: '@'

},
link: function(scope, element, attrs, ngModel) {
var dateFormat,
Expand Down Expand Up @@ -500,6 +503,60 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
if (attrs.dateDisabled) {
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
}
function compareDates(date1, date2) {
if (isNaN(date1) || isNaN(date2)) {
return undefined;
}
else {
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()) );
}
}

function getDateLimitToCheck(limitName) {
var watchDate = scope.watchData[limitName];
if (!watchDate) {
return null;
} else {
return new Date(watchDate);
}
}

function isDateLimitMet(limitName, dateToCheck, viewValue) {
var parsedDate = dateParser.parse(viewValue, dateFormat);
var dateCompare = compareDates(parsedDate, dateToCheck);
if (limitName == 'minDate') {
return !dateCompare || dateCompare > 0;
} else if (limitName == 'maxDate') {
return !dateCompare || dateCompare < 0;
}
}

function dateLimitParseFormat(limitName, viewValue, isFormatOnly) {
var dateLimit = getDateLimitToCheck(limitName);
if (dateLimit) {
var isMet = isDateLimitMet(limitName, dateLimit, viewValue);
ngModel.$setValidity(limitName, isMet);
return (isFormatOnly || isMet) ? viewValue : undefined;
} else {
return viewValue;
}
}

function minLimitParse(viewValue) {
return dateLimitParseFormat('minDate', viewValue, false);
}

function minLimitFormat(viewValue) {
return dateLimitParseFormat('minDate', viewValue, true);
}

function maxLimitParse(viewValue) {
return dateLimitParseFormat('maxDate', viewValue, false);
}

function maxLimitFormat(viewValue) {
return dateLimitParseFormat('maxDate', viewValue, true);
}

function parseDate(viewValue) {
if (!viewValue) {
Expand All @@ -523,6 +580,10 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
}
}
ngModel.$parsers.unshift(parseDate);
ngModel.$parsers.unshift(minLimitParse);
ngModel.$formatters.unshift(minLimitFormat);
ngModel.$parsers.unshift(maxLimitParse);
ngModel.$formatters.unshift(maxLimitFormat);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of parsers and formatters to be adding to the arrays. Ideally we should only be adding one parser and formatter which executes all of the modifications we desire.


ngModel.$formatters.push(function (value) {
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
Expand Down
83 changes: 81 additions & 2 deletions src/datepicker/test/datepicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1122,12 +1122,21 @@ describe('datepicker directive', function () {
$document = _$document_;
$sniffer = _$sniffer_;
$rootScope.isopen = true;
$rootScope.format='yyyy-MM-dd';
$rootScope.date = new Date('September 30, 2010 15:30:00');
var wrapElement = $compile('<div><input ng-model="date" datepicker-popup is-open="isopen"><div>')($rootScope);
$rootScope.minimumDate = new Date('January 1, 1970');
$rootScope.maximumDate = new Date('January 1, 2020');
$rootScope.dateDisabledHandler = jasmine.createSpy('dateDisabledHandler');
var wrapElement = $compile('<div><input ng-model="date" min-date = "minimumDate" max-date = "maximumDate" date-disabled="dateDisabledHandler(date, mode)" datepicker-popup="{{format}}" is-open="isopen"><div>')($rootScope);
$rootScope.$digest();
assignElements(wrapElement);

}));

it('executes the dateDisabled expression for each visible day plus one for validation', function() {
expect($rootScope.dateDisabledHandler.calls.length).toEqual(42+1);
});

it('datepicker is displayed', function() {
expect(dropdownEl).not.toBeHidden();
});
Expand Down Expand Up @@ -1161,6 +1170,41 @@ describe('datepicker directive', function () {
$rootScope.date = new Date('January 10, 1983 10:00:00');
$rootScope.$digest();
expect(inputEl.val()).toBe('1983-01-10');
expect(inputEl).not.toHaveClass('ng-invalid');
expect(inputEl).not.toHaveClass('ng-invalid-min-date');
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
});

it('updates the input correctly when model changes to a date prior to minimum date', function() {
$rootScope.date = new Date('January 10, 1963 10:00:00');
$rootScope.$digest();
expect(inputEl.val()).toBe('1963-01-10');
expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).toHaveClass('ng-invalid-min-date');
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
});

it('input is not marked with invalid minimum date when minimum is not a valid date', function() {
expect(inputEl).not.toHaveClass('ng-invalid-min-date');
$rootScope.minimumDate = 'pizza';
$rootScope.$digest();
expect(inputEl).not.toHaveClass('ng-invalid-min-date');
});

it('updates the input correctly when model changes to a date past the maximum date', function() {
$rootScope.date = new Date('January 10, 2023 10:00:00');
$rootScope.$digest();
expect(inputEl.val()).toBe('2023-01-10');
expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).toHaveClass('ng-invalid-max-date');
expect(inputEl).not.toHaveClass('ng-invalid-min-date');
});

it('input is not marked with invalid maximum date when maximum is not a valid date', function() {
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
$rootScope.maximumDate = new Date('pizza');
$rootScope.$digest();
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
});

it('closes the dropdown when a day is clicked', function() {
Expand Down Expand Up @@ -1214,7 +1258,42 @@ describe('datepicker directive', function () {
expect(inputEl).not.toHaveClass('ng-invalid-date');
});

describe('focus', function () {
it('sets `ng-invalid` for date prior to minimum date', function() {
changeInputValueTo(inputEl, '1960-12-01');

expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).toHaveClass('ng-invalid-min-date');
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
expect(inputEl).not.toHaveClass('ng-invalid-date');
expect($rootScope.date).toBeUndefined();
expect(inputEl.val()).toBe('1960-12-01');
});

it('sets `ng-invalid` for date (european format) prior to minimum date', function() {
$rootScope.format='dd.MM.yyyy';
$rootScope.$digest();
changeInputValueTo(inputEl, '20.12.1960');

expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).toHaveClass('ng-invalid-min-date');
expect(inputEl).not.toHaveClass('ng-invalid-max-date');
expect(inputEl).not.toHaveClass('ng-invalid-date');
expect($rootScope.date).toBeUndefined();
expect(inputEl.val()).toBe('20.12.1960');
});

it('sets `ng-invalid` for date past the maximum date', function() {
changeInputValueTo(inputEl, '2050-12-01');

expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).not.toHaveClass('ng-invalid-min-date');
expect(inputEl).toHaveClass('ng-invalid-max-date');
expect(inputEl).not.toHaveClass('ng-invalid-date');
expect($rootScope.date).toBeUndefined();
expect(inputEl.val()).toBe('2050-12-01');
});

describe('focus', function () {
beforeEach(function() {
var body = $document.find('body');
body.append(inputEl);
Expand Down