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

fix(ngModel): do not reset bound date objects #8912

Closed
wants to merge 1 commit into from
Closed
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
41 changes: 35 additions & 6 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
};
}

function weekParser(isoWeek) {
function weekParser(isoWeek, existingDate) {
if (isDate(isoWeek)) {
return isoWeek;
}
Expand All @@ -1015,17 +1015,29 @@ function weekParser(isoWeek) {
if (parts) {
var year = +parts[1],
week = +parts[2],
hours = 0,
minutes = 0,
seconds = 0,
milliseconds = 0,
firstThurs = getFirstThursdayOfYear(year),
addDays = (week - 1) * 7;
return new Date(year, 0, firstThurs.getDate() + addDays);

if (existingDate) {
hours = existingDate.getHours();
minutes = existingDate.getMinutes();
seconds = existingDate.getSeconds();
milliseconds = existingDate.getMilliseconds();
}

return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
}
}

return NaN;
}

function createDateParser(regexp, mapping) {
return function(iso) {
return function(iso, date) {
var parts, map;

if (isDate(iso)) {
Expand All @@ -1047,14 +1059,26 @@ function createDateParser(regexp, mapping) {

if (parts) {
parts.shift();
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0 };
if (date) {
map = {
yyyy: date.getFullYear(),
MM: date.getMonth() + 1,
dd: date.getDate(),
HH: date.getHours(),
mm: date.getMinutes(),
ss: date.getSeconds(),
sss: date.getMilliseconds()
};
} else {
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
}

forEach(parts, function(part, index) {
if (index < mapping.length) {
map[mapping[index]] = +part;
}
});
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0);
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss || 0);
}
}

Expand All @@ -1072,7 +1096,12 @@ function createDateInputType(type, regexp, parseDate, format) {
ctrl.$parsers.push(function(value) {
if (ctrl.$isEmpty(value)) return null;
if (regexp.test(value)) {
var parsedDate = parseDate(value);
var previousDate = ctrl.$modelValue;
if (previousDate && timezone === 'UTC') {
var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
previousDate = new Date(previousDate.getTime() + timezoneOffset);
}
var parsedDate = parseDate(value, previousDate);
if (timezone === 'UTC') {
parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
}
Expand Down
78 changes: 78 additions & 0 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,17 @@ describe('input', function() {
expect(scope.form.alias.$error.month).toBeTruthy();
});

it('should only change the month of a bound date', function() {
compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');

scope.$apply(function() {
scope.value = new Date(Date.UTC(2013, 7, 1, 1, 0, 0, 0));
});
changeInputValueTo('2013-12');
expect(+scope.value).toBe(Date.UTC(2013, 11, 1, 1, 0, 0, 0));
expect(inputElm.val()).toBe('2013-12');
});

describe('min', function (){
var scope;
beforeEach(inject(function ($rootScope){
Expand Down Expand Up @@ -2406,6 +2417,18 @@ describe('input', function() {
expect(inputElm.val()).toBe('2013-W02');
});

it('should not affect the hours or minutes of a bound date', function (){
compileInput('<input type="week" ng-model="secondWeek"/>');

scope.$apply(function(){
scope.secondWeek = new Date(2013, 0, 11, 1, 0, 0, 0);
});

changeInputValueTo('2013-W03');

expect(+scope.secondWeek).toBe(+new Date(2013, 0, 17, 1, 0, 0, 0));
});

it('should set the model undefined if the input is an invalid week string', function () {
compileInput('<input type="week" ng-model="value"/>');

Expand Down Expand Up @@ -2934,6 +2957,17 @@ describe('input', function() {
expect(scope.form.alias.$error.time).toBeTruthy();
});

it('should only change hours and minute of a bound date', function() {
compileInput('<input type="time" ng-model="value"" />');

scope.$apply(function(){
scope.value = new Date(2013, 2, 3, 1, 0, 0);
});

changeInputValueTo('01:02');
expect(+scope.value).toBe(+new Date(2013, 2, 3, 1, 2, 0));
});

describe('min', function (){
Copy link
Contributor

Choose a reason for hiding this comment

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

Add test for usecase: input[year] and input[moth] and input[week] in one test, set a value to each one via simulating user input and make sure the result is the combined date.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

var scope;
beforeEach(inject(function ($rootScope){
Expand Down Expand Up @@ -3141,6 +3175,50 @@ describe('input', function() {
expect(scope.form.alias.$error.date).toBeTruthy();
});

it('should work with multiple date types bound to the same model', function() {
formElm = jqLite('<form name="form"></form>');

var timeElm = jqLite('<input type="time" ng-model="val" />'),
monthElm = jqLite('<input type="month" ng-model="val" />'),
weekElm = jqLite('<input type="week" ng-model="val" />');

formElm.append(timeElm);
formElm.append(monthElm);
formElm.append(weekElm);

$compile(formElm)(scope);

scope.$apply(function() {
scope.val = new Date(2013, 1, 2, 3, 4, 5, 6);
});

expect(timeElm.val()).toBe('03:04:05');
expect(monthElm.val()).toBe('2013-02');
expect(weekElm.val()).toBe('2013-W05');

changeGivenInputTo(monthElm, '2012-02');
expect(monthElm.val()).toBe('2012-02');
expect(timeElm.val()).toBe('03:04:05');
expect(weekElm.val()).toBe('2012-W05');

changeGivenInputTo(timeElm, '04:05:06');
expect(monthElm.val()).toBe('2012-02');
expect(timeElm.val()).toBe('04:05:06');
expect(weekElm.val()).toBe('2012-W05');

changeGivenInputTo(weekElm, '2014-W01');
expect(monthElm.val()).toBe('2014-01');
expect(timeElm.val()).toBe('04:05:06');
expect(weekElm.val()).toBe('2014-W01');
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a check at the end for the expected date, including milliseconds.


expect(+scope.val).toBe(+new Date(2014, 0, 2, 4, 5, 6, 6));

function changeGivenInputTo(inputElm, value) {
inputElm.val(value);
browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
}
});

describe('min', function (){
beforeEach(function (){
compileInput('<input type="date" ng-model="value" name="alias" min="2000-01-01" />');
Expand Down