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

Commit b48fcd7

Browse files
committed
fix(ngModel): do not reset bound date objects
Closes #6666
1 parent ae952fb commit b48fcd7

File tree

2 files changed

+108
-7
lines changed

2 files changed

+108
-7
lines changed

src/ng/directive/input.js

+32-7
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
10071007
};
10081008
}
10091009

1010-
function weekParser(isoWeek) {
1010+
function weekParser(isoWeek, existingDate) {
10111011
if (isDate(isoWeek)) {
10121012
return isoWeek;
10131013
}
@@ -1020,16 +1020,33 @@ function weekParser(isoWeek) {
10201020
week = +parts[2],
10211021
firstThurs = getFirstThursdayOfYear(year),
10221022
addDays = (week - 1) * 7;
1023+
1024+
if (existingDate) {
1025+
return new Date(year, 0, firstThurs.getDate() + addDays,
1026+
existingDate.getHours(),
1027+
existingDate.getMinutes(),
1028+
existingDate.getSeconds(),
1029+
existingDate.getMilliseconds());
1030+
}
10231031
return new Date(year, 0, firstThurs.getDate() + addDays);
10241032
}
10251033
}
10261034

10271035
return NaN;
10281036
}
10291037

1038+
var DATE_METHOD_MAPPING = {
1039+
yyyy: 'FullYear',
1040+
MM: 'Month',
1041+
dd: 'Date',
1042+
HH: 'Hours',
1043+
mm: 'Minutes',
1044+
ss: 'Seconds'
1045+
};
1046+
10301047
function createDateParser(regexp, mapping) {
1031-
return function(iso) {
1032-
var parts, map;
1048+
return function(iso, date) {
1049+
var parts;
10331050

10341051
if (isDate(iso)) {
10351052
return iso;
@@ -1041,14 +1058,17 @@ function createDateParser(regexp, mapping) {
10411058

10421059
if (parts) {
10431060
parts.shift();
1044-
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0 };
1061+
if (!date) {
1062+
date = new Date(1970, 0, 1, 0, 0, 0);
1063+
}
10451064

10461065
forEach(parts, function(part, index) {
10471066
if (index < mapping.length) {
1048-
map[mapping[index]] = +part;
1067+
date['set' + DATE_METHOD_MAPPING[mapping[index]]](+part - (mapping[index] === 'MM') || 0);
10491068
}
10501069
});
1051-
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0);
1070+
1071+
return date;
10521072
}
10531073
}
10541074

@@ -1066,7 +1086,12 @@ function createDateInputType(type, regexp, parseDate, format) {
10661086
ctrl.$parsers.push(function(value) {
10671087
if (ctrl.$isEmpty(value)) return null;
10681088
if (regexp.test(value)) {
1069-
var parsedDate = parseDate(value);
1089+
var previousDate = ctrl.$modelValue;
1090+
if (previousDate) {
1091+
var timezoneOffset = (timezone === 'UTC') ? (60000 * previousDate.getTimezoneOffset()) : 0;
1092+
previousDate = new Date(previousDate.getTime() + timezoneOffset);
1093+
}
1094+
var parsedDate = parseDate(value, previousDate);
10701095
if (timezone === 'UTC') {
10711096
parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
10721097
}

test/ng/directive/inputSpec.js

+76
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,17 @@ describe('input', function() {
20622062
expect(scope.form.alias.$error.month).toBeTruthy();
20632063
});
20642064

2065+
it('should only change the month of a bound date', function() {
2066+
compileInput('<input type="month" ng-model="value" ng-model-options="{timezone: \'UTC\'}" />');
2067+
2068+
scope.$apply(function() {
2069+
scope.value = new Date(Date.UTC(2013, 7, 1, 1, 0, 0));
2070+
});
2071+
changeInputValueTo('2013-12');
2072+
expect(+scope.value).toBe(Date.UTC(2013, 11, 1, 1, 0, 0));
2073+
expect(inputElm.val()).toBe('2013-12');
2074+
});
2075+
20652076
describe('min', function (){
20662077
beforeEach(function (){
20672078
compileInput('<input type="month" ng-model="value" name="alias" min="2013-01" />');
@@ -2124,6 +2135,18 @@ describe('input', function() {
21242135
expect(inputElm.val()).toBe('2013-W02');
21252136
});
21262137

2138+
it('should not affect the hours or minutes of a bound date', function (){
2139+
compileInput('<input type="week" ng-model="secondWeek"/>');
2140+
2141+
scope.$apply(function(){
2142+
scope.secondWeek = new Date(2013, 0, 11, 1, 0, 0);
2143+
});
2144+
2145+
changeInputValueTo('2013-W03');
2146+
2147+
expect(+scope.secondWeek).toBe(+new Date(2013, 0, 17, 1, 0, 0));
2148+
});
2149+
21272150
it('should set the model undefined if the input is an invalid week string', function () {
21282151
compileInput('<input type="week" ng-model="value"/>');
21292152

@@ -2549,6 +2572,17 @@ describe('input', function() {
25492572
expect(scope.form.alias.$error.time).toBeTruthy();
25502573
});
25512574

2575+
it('should only change hours and minute of a bound date', function() {
2576+
compileInput('<input type="time" ng-model="value"" />');
2577+
2578+
scope.$apply(function(){
2579+
scope.value = new Date(2013, 2, 3, 1, 0, 0);
2580+
});
2581+
2582+
changeInputValueTo('01:02');
2583+
expect(+scope.value).toBe(+new Date(2013, 2, 3, 1, 2, 0));
2584+
});
2585+
25522586
describe('min', function (){
25532587
beforeEach(function (){
25542588
compileInput('<input type="time" ng-model="value" name="alias" min="09:30:00" />');
@@ -2717,6 +2751,48 @@ describe('input', function() {
27172751
expect(scope.form.alias.$error.date).toBeTruthy();
27182752
});
27192753

2754+
it('should work with multiple date types bound to the same model', function() {
2755+
formElm = jqLite('<form name="form"></form>');
2756+
2757+
var timeElm = jqLite('<input type="time" ng-model="val" />'),
2758+
monthElm = jqLite('<input type="month" ng-model="val" />'),
2759+
weekElm = jqLite('<input type="week" ng-model="val" />');
2760+
2761+
formElm.append(timeElm);
2762+
formElm.append(monthElm);
2763+
formElm.append(weekElm);
2764+
2765+
$compile(formElm)(scope);
2766+
2767+
scope.$apply(function() {
2768+
scope.val = new Date(2013, 1, 2, 3, 4, 5);
2769+
});
2770+
2771+
expect(timeElm.val()).toBe('03:04:05');
2772+
expect(monthElm.val()).toBe('2013-02');
2773+
expect(weekElm.val()).toBe('2013-W05');
2774+
2775+
changeGivenInputTo(monthElm, '2012-02');
2776+
expect(monthElm.val()).toBe('2012-02');
2777+
expect(timeElm.val()).toBe('03:04:05');
2778+
expect(weekElm.val()).toBe('2012-W05');
2779+
2780+
changeGivenInputTo(timeElm, '04:05:06');
2781+
expect(monthElm.val()).toBe('2012-02');
2782+
expect(timeElm.val()).toBe('04:05:06');
2783+
expect(weekElm.val()).toBe('2012-W05');
2784+
2785+
changeGivenInputTo(weekElm, '2014-W01');
2786+
expect(monthElm.val()).toBe('2014-01');
2787+
expect(timeElm.val()).toBe('04:05:06');
2788+
expect(weekElm.val()).toBe('2014-W01');
2789+
2790+
function changeGivenInputTo(inputElm, value) {
2791+
inputElm.val(value);
2792+
browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
2793+
}
2794+
});
2795+
27202796
describe('min', function (){
27212797
beforeEach(function (){
27222798
compileInput('<input type="date" ng-model="value" name="alias" min="2000-01-01" />');

0 commit comments

Comments
 (0)