Skip to content

Commit 899bde4

Browse files
committed
Integrate with ngModelController so is updated. Also pegged to 1.2.26 because 1.3 has some behavior that breaks the ngModelController tests
1 parent f1c164a commit 899bde4

File tree

7 files changed

+197
-50
lines changed

7 files changed

+197
-50
lines changed

bower.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
"test*"
2222
],
2323
"dependencies": {
24-
"angular": "^1.2.26",
24+
"angular": "1.2.26",
2525
"moment": "^2.8.3"
2626
},
2727
"devDependencies": {
28-
"angular-mocks": "^1.2.26",
28+
"angular-mocks": "1.2.26",
2929
"bootstrap": "^3.1.1",
3030
"jquery": "^2.1.1"
3131
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"grunt-contrib-jshint": "^0.10.0",
1616
"grunt-istanbul-coverage": "^0.1.0",
1717
"grunt-karma": "^0.9.0",
18-
"karma-chrome-launcher": "^0.1.2",
18+
"karma-chrome-launcher": "^0.1.5",
1919
"karma-coverage": "^0.2.1",
2020
"karma-firefox-launcher": "^0.1.3",
2121
"karma-jasmine": "^0.2.3",
2222
"karma-phantomjs-launcher": "^0.1.2",
2323
"matchdep": "^0.3.0",
24-
"phantomjs": "^1.9.7-1"
24+
"phantomjs": "^1.9.12"
2525
},
2626
"peerDependencies": {
2727
"grunt-cli": "0.1.13"

src/js/datetimepicker.js

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ angular.module('ui.bootstrap.datetimepicker', [])
9494
" <thead>" +
9595
" <tr>" +
9696
" <th class='left' data-ng-click='changeView(data.currentView, data.leftDate, $event)' data-ng-show='data.leftDate.selectable'><i class='glyphicon glyphicon-arrow-left'/></th>" +
97-
" <th class='switch' colspan='5' data-ng-show='data.currentDate.selectable' data-ng-click='changeView(data.previousView, data.currentDate, $event)'>{{ data.currentDate.display }}</th>" +
97+
" <th class='switch' colspan='5' data-ng-show='data.previousViewDate.selectable' data-ng-click='changeView(data.previousView, data.previousViewDate, $event)'>{{ data.previousViewDate.display }}</th>" +
9898
" <th class='right' data-ng-click='changeView(data.currentView, data.rightDate, $event)' data-ng-show='data.rightDate.selectable'><i class='glyphicon glyphicon-arrow-right'/></th>" +
9999
" </tr>" +
100100
" <tr>" +
@@ -124,7 +124,7 @@ angular.module('ui.bootstrap.datetimepicker', [])
124124
beforeRender: "&"
125125
},
126126
replace: true,
127-
link: function (scope, element, attrs) {
127+
link: function (scope, element, attrs, ngModelController) {
128128

129129
var directiveConfig = {};
130130

@@ -138,22 +138,28 @@ angular.module('ui.bootstrap.datetimepicker', [])
138138

139139
validateConfiguration(configuration);
140140

141+
var startOfDecade = function (unixDate) {
142+
var startYear = (parseInt(moment.utc(unixDate).year() / 10, 10) * 10);
143+
return moment.utc(unixDate).year(startYear).startOf('year');
144+
};
145+
141146
var dataFactory = {
142147
year: function (unixDate) {
143148
var selectedDate = moment.utc(unixDate).startOf('year');
144149
// View starts one year before the decade starts and ends one year after the decade ends
145150
// i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
146151
// Truncate the last digit from the current year and subtract 1 to get the start of the decade
147152
var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
148-
var startDate = moment.utc(selectedDate).year(startDecade - 1).startOf('year');
149-
var activeYear = scope.ngModel ? moment(scope.ngModel).year() : 0;
153+
var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');
154+
155+
var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;
150156

151157
var result = {
152158
'currentView': 'year',
153159
'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
154-
'currentDate': new DateObject({ dateValue: null, display: startDecade + '-' + (startDecade + 9) }),
155-
'leftDate': new DateObject({ dateValue: moment.utc(startDate).subtract(9, 'year').valueOf() }),
156-
'rightDate': new DateObject({ dateValue: moment.utc(startDate).add(11, 'year').valueOf() }),
160+
'previousViewDate': new DateObject({dateValue: null, display: startDecade + '-' + (startDecade + 9)}),
161+
'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(9, 'year').valueOf()}),
162+
'rightDate': new DateObject({dateValue: moment.utc(startDate).add(11, 'year').valueOf()}),
157163
'dates': []
158164
};
159165

@@ -176,16 +182,19 @@ angular.module('ui.bootstrap.datetimepicker', [])
176182
month: function (unixDate) {
177183

178184
var startDate = moment.utc(unixDate).startOf('year');
179-
180-
var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM') : 0;
185+
var previousViewDate = startOfDecade(unixDate);
186+
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;
181187

182188
var result = {
183189
'previousView': 'year',
184190
'currentView': 'month',
185191
'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
186-
'currentDate': new DateObject({ dateValue: startDate.valueOf(), display: startDate.format('YYYY') }),
187-
'leftDate': new DateObject({ dateValue: moment.utc(startDate).subtract(1, 'year').valueOf() }),
188-
'rightDate': new DateObject({ dateValue: moment.utc(startDate).add(1, 'year').valueOf() }),
192+
'previousViewDate': new DateObject({
193+
dateValue: previousViewDate.valueOf(),
194+
display: startDate.format('YYYY')
195+
}),
196+
'leftDate': new DateObject({dateValue: moment.utc(startDate).subtract(1, 'year').valueOf()}),
197+
'rightDate': new DateObject({dateValue: moment.utc(startDate).add(1, 'year').valueOf()}),
189198
'dates': []
190199
};
191200

@@ -207,19 +216,23 @@ angular.module('ui.bootstrap.datetimepicker', [])
207216

208217
var selectedDate = moment.utc(unixDate);
209218
var startOfMonth = moment.utc(selectedDate).startOf('month');
219+
var previousViewDate = moment.utc(selectedDate).startOf('year');
210220
var endOfMonth = moment.utc(selectedDate).endOf('month');
211221

212222
var startDate = moment.utc(startOfMonth).subtract(Math.abs(startOfMonth.weekday()), 'days');
213223

214-
var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM-DD') : '';
224+
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';
215225

216226
var result = {
217227
'previousView': 'month',
218228
'currentView': 'day',
219229
'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
220-
'currentDate': new DateObject({ dateValue: startDate.valueOf(), display: selectedDate.format('YYYY-MMM') }),
221-
'leftDate': new DateObject({ dateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf() }),
222-
'rightDate': new DateObject({ dateValue: moment.utc(startOfMonth).add(1, 'months').valueOf() }),
230+
'previousViewDate': new DateObject({
231+
dateValue: previousViewDate.valueOf(),
232+
display: startOfMonth.format('YYYY-MMM')
233+
}),
234+
'leftDate': new DateObject({dateValue: moment.utc(startOfMonth).subtract(1, 'months').valueOf()}),
235+
'rightDate': new DateObject({dateValue: moment.utc(startOfMonth).add(1, 'months').valueOf()}),
223236
'dayNames': [],
224237
'weeks': []
225238
};
@@ -249,17 +262,21 @@ angular.module('ui.bootstrap.datetimepicker', [])
249262
},
250263

251264
hour: function (unixDate) {
252-
var selectedDate = moment.utc(unixDate).hour(0).minute(0).second(0);
265+
var selectedDate = moment.utc(unixDate).startOf('day');
266+
var previousViewDate = moment.utc(selectedDate).startOf('month');
253267

254-
var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H') : '';
268+
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';
255269

256270
var result = {
257271
'previousView': 'day',
258272
'currentView': 'hour',
259273
'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
260-
'currentDate': new DateObject({ dateValue: selectedDate.valueOf(), display: selectedDate.format('ll') }),
261-
'leftDate': new DateObject({ dateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf() }),
262-
'rightDate': new DateObject({ dateValue: moment.utc(selectedDate).add(1, 'days').valueOf() }),
274+
'previousViewDate': new DateObject({
275+
dateValue: previousViewDate.valueOf(),
276+
display: selectedDate.format('ll')
277+
}),
278+
'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'days').valueOf()}),
279+
'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'days').valueOf()}),
263280
'dates': []
264281
};
265282

@@ -278,17 +295,20 @@ angular.module('ui.bootstrap.datetimepicker', [])
278295
},
279296

280297
minute: function (unixDate) {
281-
var selectedDate = moment.utc(unixDate).minute(0).second(0);
282-
283-
var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H:mm') : '';
298+
var selectedDate = moment.utc(unixDate).startOf('hour');
299+
var previousViewDate = moment.utc(selectedDate).startOf('day');
300+
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';
284301

285302
var result = {
286303
'previousView': 'hour',
287304
'currentView': 'minute',
288305
'nextView': 'setTime',
289-
'currentDate': new DateObject({ dateValue: selectedDate.valueOf(), display: selectedDate.format('lll') }),
290-
'leftDate': new DateObject({ dateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf() }),
291-
'rightDate': new DateObject({ dateValue: moment.utc(selectedDate).add(1, 'hours').valueOf() }),
306+
'previousViewDate': new DateObject({
307+
dateValue: previousViewDate.valueOf(),
308+
display: selectedDate.format('lll')
309+
}),
310+
'leftDate': new DateObject({dateValue: moment.utc(selectedDate).subtract(1, 'hours').valueOf()}),
311+
'rightDate': new DateObject({dateValue: moment.utc(selectedDate).add(1, 'hours').valueOf()}),
292312
'dates': []
293313
};
294314

@@ -312,21 +332,21 @@ angular.module('ui.bootstrap.datetimepicker', [])
312332
var tempDate = new Date(unixDate);
313333
var newDate = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));
314334

315-
var oldDate = scope.ngModel;
316-
scope.ngModel = newDate;
335+
var oldDate = ngModelController.$modelValue;
336+
ngModelController.$setViewValue(newDate);
317337

318338
if (configuration.dropdownSelector) {
319339
jQuery(configuration.dropdownSelector).dropdown('toggle');
320340
}
321341

322-
scope.onSetTime({newDate: scope.ngModel, oldDate: oldDate});
342+
scope.onSetTime({newDate: newDate, oldDate: oldDate});
323343

324344
return dataFactory[configuration.startView](unixDate);
325345
}
326346
};
327347

328-
var getUTCTime = function () {
329-
var tempDate = (scope.ngModel ? moment(scope.ngModel).toDate() : new Date());
348+
var getUTCTime = function (modelValue) {
349+
var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
330350
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
331351
};
332352

@@ -354,19 +374,28 @@ angular.module('ui.bootstrap.datetimepicker', [])
354374
$view: result.currentView,
355375
$dates: result.dates || weekDates,
356376
$leftDate: result.leftDate,
357-
$upDate: result.currentDate,
377+
$upDate: result.previousViewDate,
358378
$rightDate: result.rightDate
359379
});
360380

361381
scope.data = result;
362382
}
363383
};
364384

365-
scope.changeView(configuration.startView, new DateObject({dateValue: getUTCTime(), selectable: true}));
385+
// While is **seems** like the next line should use ngModelController.$modelValue rather than scope.ngModel,
386+
// in fact ngModelController.$modelValue is always NaN for the first call (even if ngModel has a value),
387+
// resulting in an initial rendering with the current date. (at least on angular 1.2.26)
388+
// This results in a call to the beforeRender callback with today's date as the model value when the developer
389+
// would reasonably expect the current model value.
390+
391+
scope.changeView(configuration.startView, new DateObject({
392+
dateValue: getUTCTime(scope.ngModel),
393+
selectable: true
394+
}));
366395

367-
scope.$watch('ngModel', function () {
368-
scope.changeView(scope.data.currentView, new DateObject({dateValue: getUTCTime()}));
369-
});
396+
ngModelController.$render = function () {
397+
scope.changeView(scope.data.currentView, new DateObject({dateValue: getUTCTime(ngModelController.$modelValue)}));
398+
};
370399
}
371400
};
372401
}]);

test/configuration/beforeRender.spec.js

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('beforeRender', function () {
1818
beforeEach(inject(function (_$compile_, _$rootScope_) {
1919
$compile = _$compile_;
2020
$rootScope = _$rootScope_;
21-
$rootScope.date = null;
21+
// $rootScope.date = null;
2222
}));
2323

2424
describe('does not throw exception', function () {
@@ -147,7 +147,6 @@ describe('beforeRender', function () {
147147
$rootScope.date = moment("2008-01-01T00:00:00.000").toDate();
148148
$rootScope.beforeRender = function ($leftDate) {
149149
expect($leftDate).not.toBeUndefined();
150-
expect(moment($leftDate.dateValue).toDate()).toEqual(moment.utc("1990-01-01T00:00:00.000").toDate());
151150
};
152151

153152
spyOn($rootScope, 'beforeRender').and.callThrough();
@@ -159,6 +158,7 @@ describe('beforeRender', function () {
159158
selectedElement.trigger('click');
160159

161160
expect($rootScope.beforeRender).toHaveBeenCalled();
161+
expect($rootScope.beforeRender.calls.argsFor(0)[0].dateValue).toEqual(631152000000); // 1990-01-01
162162
});
163163

164164

@@ -167,7 +167,6 @@ describe('beforeRender', function () {
167167
$rootScope.date = moment("2014-04-01T00:00:00.000").toDate();
168168
$rootScope.beforeRender = function ($rightDate) {
169169
expect($rightDate).not.toBeUndefined();
170-
expect(moment($rightDate.dateValue).toDate()).toEqual(moment.utc("2015-01-01T00:00:00.000").toDate());
171170
};
172171

173172
spyOn($rootScope, 'beforeRender').and.callThrough();
@@ -179,29 +178,84 @@ describe('beforeRender', function () {
179178
selectedElement.trigger('click');
180179

181180
expect($rootScope.beforeRender).toHaveBeenCalled();
181+
expect($rootScope.beforeRender.calls.argsFor(0)[0].dateValue).toEqual(1420070400000); // 2015-01-01
182182
});
183183

184-
it('$upDate parameter is the beginning of the next view up ', function () {
184+
185+
it('startview = "day" and $upDate parameter is the beginning of the next view up ', function () {
185186

186187
// i.e. minute -> hour -> day -> month -> year
187188

188189
$rootScope.date = moment("2014-10-18T13:00:00.000").toDate();
190+
189191
$rootScope.beforeRender = function ($upDate) {
190192
expect($upDate).not.toBeUndefined();
191-
expect(moment($upDate.dateValue).toDate()).toEqual(moment.utc("2014-10-18T00:00:00.000").toDate());
192193
};
193194

194195
spyOn($rootScope, 'beforeRender').and.callThrough();
195196

196-
var element = $compile('<datetimepicker data-ng-model=\'date\' data-before-render=\'beforeRender($upDate)\' data-datetimepicker-config="{ startView: \'hour\', minView: \'hour\' }" ></datetimepicker>')($rootScope);
197+
var element = $compile('<datetimepicker data-ng-model=\'date\' data-before-render=\'beforeRender($upDate)\' data-datetimepicker-config="{ startView: \'day\', minView: \'day\' }" ></datetimepicker>')($rootScope);
197198
$rootScope.$digest();
198199

200+
expect($rootScope.beforeRender).toHaveBeenCalled();
201+
202+
expect($rootScope.beforeRender.calls.argsFor(0)[0].dateValue).toEqual(1388534400000); // 2014-01-01 - the start of the 'month' view.
203+
204+
205+
var selectedElement = jQuery(jQuery('.day', element)[2]);
206+
selectedElement.trigger('click');
207+
208+
});
209+
210+
211+
it('startview = "hour" and $upDate parameter is the beginning of the next view up ', function () {
212+
213+
// i.e. minute -> hour -> day -> month -> year
214+
215+
var $scope = $rootScope.$new();
216+
217+
218+
$scope.date = moment("2014-10-18T13:00:00.000").toDate();
219+
220+
$scope.beforeRender = function ($upDate) {
221+
expect($upDate).not.toBeUndefined();
222+
};
223+
224+
spyOn($scope, 'beforeRender').and.callThrough();
225+
226+
var element = $compile('<datetimepicker data-ng-model=\'date\' data-before-render=\'beforeRender($upDate)\' data-datetimepicker-config="{ startView: \'hour\', minView: \'hour\' }" ></datetimepicker>')($scope);
227+
$rootScope.$digest();
228+
229+
expect($scope.beforeRender).toHaveBeenCalled();
230+
expect($scope.beforeRender.calls.argsFor(0)[0].dateValue).toEqual(1412121600000); // 2014-10-01 12:00 the start of the 'day' view
231+
199232
var selectedElement = jQuery(jQuery('.hour', element)[2]);
200233
selectedElement.trigger('click');
201234

202-
expect($rootScope.beforeRender).toHaveBeenCalled();
203235
});
204236

237+
it('startview = "minute" and $upDate parameter is the beginning of the next view up ', function () {
238+
239+
// i.e. minute -> hour -> day -> month -> year
240+
241+
$rootScope.date = moment("2014-10-18T13:00:00.000").toDate();
242+
$rootScope.beforeRender = function ($upDate) {
243+
expect($upDate).not.toBeUndefined();
244+
};
245+
246+
spyOn($rootScope, 'beforeRender').and.callThrough();
247+
248+
var element = $compile('<datetimepicker data-ng-model=\'date\' data-before-render=\'beforeRender($upDate)\' data-datetimepicker-config="{ startView: \'minute\', minView: \'minute\' }" ></datetimepicker>')($rootScope);
249+
$rootScope.$digest();
250+
251+
expect($rootScope.beforeRender).toHaveBeenCalled();
252+
expect($rootScope.beforeRender.calls.argsFor(0)[0].dateValue).toEqual(1413590400000); // 2014-10-18 00:00 Z
253+
254+
255+
var selectedElement = jQuery(jQuery('.minute', element)[2]);
256+
selectedElement.trigger('click');
257+
258+
});
205259

206260
it('year view and 2001 date is disabled', function () {
207261

0 commit comments

Comments
 (0)