Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
fix(datepicker): use $setViewValue for inner changes
Browse files Browse the repository at this point in the history
Closes #855
  • Loading branch information
bekos authored and pkozlowski-opensource committed Sep 3, 2013
1 parent 1f89fd4 commit dd99f35
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 37 deletions.
78 changes: 44 additions & 34 deletions src/datepicker/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,23 +272,6 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
scope.$destroy();
});

function formatDate(value) {
return (value) ? dateFilter(value, dateFormat) : null;
}
ngModel.$formatters.push(formatDate);

// TODO: reverse from dateFilter string to Date object
function parseDate(value) {
if ( value ) {
var date = new Date(value);
if (!isNaN(date)) {
return date;
}
}
return value;
}
ngModel.$parsers.push(parseDate);

var getIsOpen, setIsOpen;
if ( attrs.isOpen ) {
getIsOpen = $parse(attrs.isOpen);
Expand Down Expand Up @@ -333,33 +316,58 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
}

var $setModelValue = $parse(attrs.ngModel).assign;
// TODO: reverse from dateFilter string to Date object
function parseDate(viewValue) {
if (!viewValue) {
ngModel.$setValidity('date', true);
return null;
} else if (angular.isDate(viewValue)) {
ngModel.$setValidity('date', true);
return viewValue;
} else if (angular.isString(viewValue)) {
var date = new Date(viewValue);
if (isNaN(date)) {
ngModel.$setValidity('date', false);
return undefined;
} else {
ngModel.$setValidity('date', true);
return date;
}
} else {
ngModel.$setValidity('date', false);
return undefined;
}
}
ngModel.$parsers.unshift(parseDate);

// Inner change
scope.dateSelection = function() {
$setModelValue(originalScope, scope.date);
ngModel.$setViewValue(scope.date);
ngModel.$render();

if (closeOnDateSelection) {
setOpen( false );
}
};

element.bind('input change keyup', function() {
scope.$apply(function() {
updateCalendar();
});
});

// Outter change
scope.$watch(function() {
return ngModel.$modelValue;
}, function(value) {
if (angular.isString(value)) {
var date = parseDate(value);

if (value && !date) {
$setModelValue(originalScope, null);
throw new Error(value + ' cannot be parsed to a date object.');
} else {
value = date;
}
}
scope.date = value;
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
element.val(date);

updateCalendar();
};

function updateCalendar() {
scope.date = ngModel.$modelValue;
updatePosition();
});
}

function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
if (attribute) {
Expand Down Expand Up @@ -409,6 +417,8 @@ function ($compile, $parse, $document, $position, dateFilter, datepickerPopupCon
}
});

var $setModelValue = $parse(attrs.ngModel).assign;

scope.today = function() {
$setModelValue(originalScope, new Date());
};
Expand Down
57 changes: 54 additions & 3 deletions src/datepicker/test/datepicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,12 @@ describe('datepicker directive', function () {
expect($rootScope.date).toEqual(new Date('September 15, 2010 15:30:00'));
});

it('should mark the input field dirty when a day is clicked', function() {
expect(inputEl).toHaveClass('ng-pristine');
clickOption(2, 3);
expect(inputEl).toHaveClass('ng-dirty');
});

it('updates the input correctly when model changes', function() {
$rootScope.date = new Date("January 10, 1983 10:00:00");
$rootScope.$digest();
Expand All @@ -1014,11 +1020,22 @@ describe('datepicker directive', function () {
expect(dropdownEl.css('display')).toBe('none');
});

it('updates the model when input value changes', function() {
it('updates the model & calendar when input value changes', function() {
changeInputValueTo(inputEl, 'March 5, 1980');

expect($rootScope.date.getFullYear()).toEqual(1980);
expect($rootScope.date.getMonth()).toEqual(2);
expect($rootScope.date.getDate()).toEqual(5);

expect(getOptions()).toEqual([
['24', '25', '26', '27', '28', '29', '01'],
['02', '03', '04', '05', '06', '07', '08'],
['09', '10', '11', '12', '13', '14', '15'],
['16', '17', '18', '19', '20', '21', '22'],
['23', '24', '25', '26', '27', '28', '29'],
['30', '31', '01', '02', '03', '04', '05']
]);
expectSelectedElement( 1, 3 );
});

it('closes when click outside of calendar', function() {
Expand Down Expand Up @@ -1074,7 +1091,7 @@ describe('datepicker directive', function () {
});
});

describe('use with ng-required directive', function() {
describe('use with `ng-required` directive', function() {
beforeEach(inject(function() {
$rootScope.date = '';
var wrapElement = $compile('<div><input ng-model="date" datepicker-popup ng-required="true"><div>')($rootScope);
Expand All @@ -1092,8 +1109,42 @@ describe('datepicker directive', function () {
});
});

});
describe('use with `ng-change` directive', function() {
beforeEach(inject(function() {
$rootScope.changeHandler = jasmine.createSpy('changeHandler');
$rootScope.date = new Date();
var wrapElement = $compile('<div><input ng-model="date" datepicker-popup ng-required="true" ng-change="changeHandler()"><div>')($rootScope);
$rootScope.$digest();
assignElements(wrapElement);
}));

it('should not be called initially', function() {
expect($rootScope.changeHandler).not.toHaveBeenCalled();
});

it('should be called when a day is clicked', function() {
clickOption(2, 3);
expect($rootScope.changeHandler).toHaveBeenCalled();
});

it('should not be called when model changes programatically', function() {
$rootScope.date = new Date();
$rootScope.$digest();
expect($rootScope.changeHandler).not.toHaveBeenCalled();
});
});

describe('to invalid input', function() {
it('sets `ng-invalid`', function() {
changeInputValueTo(inputEl, 'pizza');

expect(inputEl).toHaveClass('ng-invalid');
expect(inputEl).toHaveClass('ng-invalid-date');
expect($rootScope.date).toBeUndefined();
expect(inputEl.val()).toBe('pizza');
});
});
});
});

describe('datepicker directive with empty initial state', function () {
Expand Down

0 comments on commit dd99f35

Please sign in to comment.