Skip to content

Commit 05e2de7

Browse files
committed
feat(modelType): modelType specifies the data type to use when storing data in the model
Accepts any string value, but the following values have special meaning (these values are case sensitive) : * ```'Date'``` stores a Date instance in the model. Will accept Date, moment, milliseconds, and ISO 8601 strings as initial input from the model * ```'moment'``` stores a moment instance in the model. Accepts the same initial values as ```Date``` * ```'milliseconds'``` store the epoch milliseconds (since 1-1-1970) in the model. Accepts the same initial values as ```Date``` -Any other value is considered a format string. -When accepting values from, and saving values to the model, this directive tries to be as flexible as possible. Dates, moments, and milliseconds are all accepted as input no matter what you specify for ```modelType```. However, strings are problematic and often lose timezone information, so use caution when storing strings. -If you must use a string, be aware that the format stored in the model must exactly match the format specified in ```modelType```. For example, the value in the model is ```'2015-12-31'``` then using ```modelType: 'MM-DD-YYYY'``` will cause an exception. -I predict that I will regret adding this feature. closes #241, closes #190, closes #39
1 parent 3475002 commit 05e2de7

File tree

4 files changed

+415
-24
lines changed

4 files changed

+415
-24
lines changed

README.md

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,33 +147,33 @@ which is specified in the data-datetimepicker-config attribute.
147147

148148
### startView
149149

150-
String. Default: 'day'
150+
String. Default: ```'day'```
151151

152152
The view that the datetimepicker should show when it is opened.
153153
Accepts values of :
154-
* 'minute' for the minute view
155-
* 'hour' for the hour view
156-
* 'day' for the day view (the default)
157-
* 'month' for the 12-month view
158-
* 'year' for the 10-year overview. Useful for date-of-birth datetimepickers.
154+
* ```'minute'``` for the minute view
155+
* ```'hour'``` for the hour view
156+
* ```'day'``` for the day view (the default)
157+
* ```'month'``` for the 12-month view
158+
* ```'year'``` for the 10-year overview. Useful for date-of-birth datetimepickers.
159159

160160
### minView
161161

162-
String. 'minute'
162+
String. ```'minute'```
163163

164164
The lowest view that the datetimepicker should show.
165165

166166
Accepts the same values as startView.
167167

168168
### minuteStep
169169

170-
Number. Default: 5
170+
Number. Default: ```5```
171171

172172
The increment used to build the hour view. A button is created for each <code>minuteStep</code> minutes.
173173

174174
### configureOn
175175

176-
String. Default: null
176+
String. Default: ```null```
177177

178178
Causes the date/time picker to re-read its configuration when the specified event is received.
179179

@@ -182,13 +182,36 @@ new configuration to be used. You can $broadcast the event to cause this directi
182182

183183
### renderOn
184184

185-
String. Default: null
185+
String. Default: ```null```
186186

187187
Causes the date/time picker to re-render its view when the specified event is received.
188188

189189
For example, if you want to disable any dates or times that are in the past.
190190
You can $broadcast the event at an interval to disable times in the past (or any other time valid dates change).
191191

192+
### modelType
193+
194+
String. Default: ```'Date'```
195+
196+
Accepts any string value, but the following values have special meaning (these values are case sensitive) :
197+
* ```'Date'``` stores a Date instance in the model. Will accept Date, moment, milliseconds, and ISO 8601 strings as initial input from the model
198+
* ```'moment'``` stores a moment instance in the model. Accepts the same initial values as ```Date```
199+
* ```'milliseconds'``` store the epoch milliseconds (since 1-1-1970) in the model. Accepts the same initial values as ```Date```
200+
201+
Any other value is considered a format string.
202+
203+
When accepting values from, and saving values to the model, this directive tries to be as flexible as possible.
204+
Dates, moments, and milliseconds are all accepted as input no matter what you specify for ```modelType```.
205+
However, strings are problematic and often lose timezone information, so use caution when storing strings.
206+
207+
If you must use a string, be aware that the format stored in the model must exactly match the format specified in ```modelType```.
208+
For example, the value in the model is ```'2015-12-31'``` then using ```modelType: 'MM-DD-YYYY'``` will cause an exception.
209+
210+
NOTA BENE: If the only reason you are storing strings is to have it property formatted when displaying to the user,
211+
please review the documentation on ngModelController $formatters and $parsers. These allow you to store a value in the model
212+
but display it formatted as you like in the view. In other words, stop it! =)
213+
214+
192215
### dropdownSelector
193216

194217
When used within a Bootstrap dropdown and jQuery, the selector specified in dropdownSelector will toggle the dropdown when a date/time is selected.
@@ -275,7 +298,7 @@ moment.locale('en'); // English
275298
moment.locale('zh-cn'); // Simplified chinese
276299
```
277300

278-
# Screenshots
301+
# Screen shots
279302

280303
## Year view
281304

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"autoprefixer": "^6.1.0",
2929
"bootstrap": "^3.3.5",
3030
"bower": "latest",
31-
"commitizen": "^2.4.4",
31+
"commitizen": "^2.4.6",
3232
"csscomb": "^3.1.8",
3333
"csslint": "^0.10.0",
3434
"cz-conventional-changelog": "^1.1.4",
@@ -77,4 +77,4 @@
7777
"path": "./node_modules/cz-conventional-changelog"
7878
}
7979
}
80-
}
80+
}

src/js/datetimepicker.js

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
dropdownSelector: null,
3333
minuteStep: 5,
3434
minView: 'minute',
35+
modelType: 'Date',
36+
parseFormat: 'YYYY-MM-DDTHH:mm:ss.SSSZZ',
3537
renderOn: null,
3638
startView: 'day'
3739
})
@@ -67,6 +69,8 @@
6769
'dropdownSelector',
6870
'minuteStep',
6971
'minView',
72+
'modelType',
73+
'parseFormat',
7074
'renderOn',
7175
'startView'
7276
];
@@ -111,6 +115,16 @@
111115
if (configuration.renderOn !== null && configuration.renderOn.length < 1) {
112116
throw ('renderOn must not be an empty string');
113117
}
118+
if (configuration.modelType !== null && !angular.isString(configuration.modelType)) {
119+
throw ('modelType must be a string');
120+
}
121+
if (configuration.modelType !== null && configuration.modelType.length < 1) {
122+
throw ('modelType must not be an empty string');
123+
}
124+
if (configuration.modelType !== 'Date' && configuration.modelType !== 'moment' && configuration.modelType !== 'milliseconds') {
125+
// modelType contains string format, overriding parseFormat with modelType;
126+
configuration.parseFormat = configuration.modelType;
127+
}
114128
if (configuration.dropdownSelector !== null && !angular.isString(configuration.dropdownSelector)) {
115129
throw ('dropdownSelector must be a string');
116130
}
@@ -187,6 +201,49 @@
187201
return moment.utc(unixDate).year(startYear).startOf('year');
188202
};
189203

204+
var formatValue = function formatValue(timeValue, formatString) {
205+
if (timeValue) {
206+
return getMoment(timeValue).format(formatString);
207+
} else {
208+
return '';
209+
}
210+
};
211+
212+
/**
213+
* Converts a time value into a moment.
214+
*
215+
* This function is now necessary because moment logs a warning when parsing a string without a format.
216+
* @param modelValue
217+
* a time value in any of the supported formats (Date, moment, milliseconds, and string)
218+
* @returns {moment}
219+
* representing the specified time value.
220+
*/
221+
222+
var getMoment = function getMoment(modelValue) {
223+
return moment(modelValue, angular.isString(modelValue) ? configuration.parseFormat : undefined);
224+
};
225+
226+
/**
227+
* Converts a time value to UCT/GMT time.
228+
* @param modelValue
229+
* a time value in any of the supported formats (Date, moment, milliseconds, and string)
230+
* @returns {number}
231+
* number of milliseconds since 1/1/1970
232+
*/
233+
234+
var getUTCTime = function getUTCTime(modelValue) {
235+
var tempDate = new Date();
236+
if (modelValue) {
237+
var tempMoment = getMoment(modelValue);
238+
if (tempMoment.isValid()) {
239+
tempDate = tempMoment.toDate();
240+
} else {
241+
throw 'Invalid date: ' + modelValue;
242+
}
243+
}
244+
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
245+
};
246+
190247
var dataFactory = {
191248
year: function year(unixDate) {
192249
var selectedDate = moment.utc(unixDate).startOf('year');
@@ -196,7 +253,7 @@
196253
var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
197254
var startDate = moment.utc(startOfDecade(unixDate)).subtract(1, 'year').startOf('year');
198255

199-
var activeYear = ngModelController.$modelValue ? moment(ngModelController.$modelValue).year() : 0;
256+
var activeYear = formatValue(ngModelController.$modelValue, 'YYYY');
200257

201258
var result = {
202259
'currentView': 'year',
@@ -217,7 +274,7 @@
217274
'display': yearMoment.format('YYYY'),
218275
'past': yearMoment.year() < startDecade,
219276
'future': yearMoment.year() > startDecade + 9,
220-
'active': yearMoment.year() === activeYear
277+
'active': yearMoment.format('YYYY') === activeYear
221278
};
222279

223280
result.dates.push(new DateObject(dateValue));
@@ -230,7 +287,7 @@
230287

231288
var startDate = moment.utc(unixDate).startOf('year');
232289
var previousViewDate = startOfDecade(unixDate);
233-
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM') : 0;
290+
var activeDate = formatValue(ngModelController.$modelValue, 'YYYY-MMM');
234291

235292
var result = {
236293
'previousView': 'year',
@@ -268,7 +325,7 @@
268325

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

271-
var activeDate = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MMM-DD') : '';
328+
var activeDate = formatValue(ngModelController.$modelValue, 'YYYY-MMM-DD');
272329

273330
var result = {
274331
'previousView': 'month',
@@ -312,7 +369,7 @@
312369
var selectedDate = moment.utc(unixDate).startOf('day');
313370
var previousViewDate = moment.utc(selectedDate).startOf('month');
314371

315-
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H') : '';
372+
var activeFormat = formatValue(ngModelController.$modelValue, 'YYYY-MM-DD H');
316373

317374
var result = {
318375
'previousView': 'day',
@@ -344,7 +401,7 @@
344401
minute: function minute(unixDate) {
345402
var selectedDate = moment.utc(unixDate).startOf('hour');
346403
var previousViewDate = moment.utc(selectedDate).startOf('day');
347-
var activeFormat = ngModelController.$modelValue ? moment(ngModelController.$modelValue).format('YYYY-MM-DD H:mm') : '';
404+
var activeFormat = formatValue(ngModelController.$modelValue, 'YYYY-MM-DD H:mm');
348405

349406
var result = {
350407
'previousView': 'hour',
@@ -379,6 +436,20 @@
379436
var tempDate = new Date(unixDate);
380437
var newDate = new Date(tempDate.getUTCFullYear(), tempDate.getUTCMonth(), tempDate.getUTCDate(), tempDate.getUTCHours(), tempDate.getUTCMinutes(), tempDate.getUTCSeconds(), tempDate.getUTCMilliseconds());
381438

439+
switch (configuration.modelType) {
440+
case 'Date':
441+
// No additional work needed
442+
break;
443+
case 'moment':
444+
newDate = moment(newDate);
445+
break;
446+
case 'milliseconds':
447+
newDate = unixDate;
448+
break;
449+
default: // It is assumed that the modelType is a formatting string.
450+
newDate = moment(newDate).format(configuration.modelType);
451+
}
452+
382453
var oldDate = ngModelController.$modelValue;
383454
ngModelController.$setViewValue(newDate);
384455

@@ -392,11 +463,6 @@
392463
}
393464
};
394465

395-
var getUTCTime = function getUTCTime(modelValue) {
396-
var tempDate = (modelValue ? moment(modelValue).toDate() : new Date());
397-
return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
398-
};
399-
400466
scope.changeView = function changeView(viewName, dateObject, event) {
401467
if (event) {
402468
event.stopPropagation();

0 commit comments

Comments
 (0)