Reputation: 9261
I have spent sometime on researching the existing datetime directives of angularjs.
Both ngularUI and AngularStrap do not provide a datetimepicker as I needed. Of course, I know use a datepicker and timepicker together to archive the purpose.
I have searched the related topic from internet and stackoverflow. Found some interesting and helpful info.
http://dalelotts.github.io/angular-bootstrap-datetimepicker/, there is a datetimepicker, but I dislike the usage of this directive.
connecting datetimepicker to angularjs , this topic is very helpful, I tried to wrap my datetimepicker directive following the steps.
My work is based on https://github.com/Eonasdan/bootstrap-datetimepicker, a bootstrap 3 based datetimepicker, the UI is very nice.
app.directive('datetimepicker', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
console.log('call datetimepicker link...');
var picker = element.datetimepicker({
dateFormat: 'dd/MM/yyyy hh:mm:ss'
});
//ngModelCtrl.$setViewValue(picker.getDate());
//model->view
ngModelCtrl.$render(function() {
console.log('ngModelCtrl.$viewValue@'+ngModelCtrl.$viewValue);
picker.setDate(ngModelCtrl.$viewValue || '');
});
//view->model
picker.on('dp.change', function(e) {
console.log('dp.change'+e.date);
scope.$apply(function(){
ngModelCtrl.$setViewValue(e.date);
});
});
}
};
});
And use it in my view.
<div class="col-md-6">
<div class="input-group date" id="endTime" data-datetimepicker ng-model="stat.endTime" data-date-format="MM/DD/YYYY hh:mm A/PM" >
<input class="form-control" type="text" placeholder="End"/>
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
There are some problems I found.
Thanks in advance.
Upvotes: 5
Views: 18940
Reputation: 2650
I ended up putting the input inside of a div and bind model to that. It also includes bootstrapvalidator (Sorry, I didn't have time to make the code perfect, but you should get the idea)
Sample html code:
<div datetimepicker="{pickTime: false}" data-ng-model="model.Birthday"><input type="text" name="Birthday" class="form-control" data-date-format="YYYY/MM/DD" data-mask="0000/00/00" data-bv-date="true" data-bv-date-format="YYYY/MM/DD" /></div>
And javascript code:
app.directive('datetimepicker', function ($timeout) {
return {
// Restrict it to be an attribute in this case
restrict: 'AE',
// optionally hook-in to ngModel's API
require: '?ngModel',
// responsible for registering DOM listeners as well as updating the DOM
link: function ($scope, element, $attrs, ngModel) {
var $element;
$timeout(function () {
$element = $(element).find("input").datetimepicker($scope.$eval($attrs.datetimepicker));
var DateTimePicker = $element.data("DateTimePicker");
DateTimePicker.setValueAngular = function (newValue) {
this.angularSetValue = true; // a lock object to prevent calling change trigger of input to fix the re-cursive call of changing values
this.setDate(newValue);
this.angularSetValue = false;
}
if (!ngModel) { return; }//below this we interact with ngModel's controller
$scope.$watch($attrs['ngModel'], function (newValue) {
if (newValue)
if (newValue != "Invalid date")
{
DateTimePicker.setValueAngular(newValue);
}
});
ngModel.$formatters.push(function (value) {
// formatting the value to be shown to the user
var format = DateTimePicker.format;
var date = moment(value);
if (date.isValid()) {
return date.format(format);
}
return '';
});
ngModel.$parsers.push(function toModel(input) {
// format user input to be used in code (converting to unix epoch or ...)
var modifiedInput = moment(input).format();
return modifiedInput;
});
//update ngModel when UI changes
$element.on('dp.change', function (e) {
if (DateTimePicker.angularSetValue === true)
return;
var newValue = $element[0].value;
if (newValue !== ngModel.$viewValue)
$scope.$apply(function () {
ngModel.$setViewValue(newValue);
});
//bootstrapvalidator support
if ($element.attr('data-bv-field') !== undefined) // if the field had validation
$element.closest("form").bootstrapValidator('revalidateField', $element);
});
});
}
};
}); // directive end
Upvotes: 0
Reputation: 11275
I really struggled long with Eonasdan datetime picker. Most of the solutions published on the web work ok or not-so ok.
In the end I merged some of the solutions I have found online. I wrapped it in a working plunker: http://plnkr.co/n8L8UZ
The directive works using ng-model
in moment
format, what is more it allows two functions to be passed:
onDateChangeFunction
and onDateClickFunction
which are called respectively.
Happy using!
The directive source code:
angular
.module('plunker')
.directive('datetimepicker', [
'$timeout',
function($timeout) {
return {
require: '?ngModel',
restrict: 'EA',
scope: {
datetimepickerOptions: '@',
onDateChangeFunction: '&',
onDateClickFunction: '&'
},
link: function($scope, $element, $attrs, controller) {
$element.on('dp.change', function() {
$timeout(function() {
var dtp = $element.data('DateTimePicker');
controller.$setViewValue(dtp.date());
$scope.onDateChangeFunction();
});
});
$element.on('click', function() {
$scope.onDateClickFunction();
});
controller.$render = function() {
if (!!controller && !!controller.$viewValue) {
var result = controller.$viewValue;
$element.data('DateTimePicker').date(result);
}
};
$element.datetimepicker($scope.$eval($attrs.datetimepickerOptions));
}
};
}
]);
Upvotes: 1
Reputation: 591
I am also using DateTimePicker by Angular Directive and it is working fine here. I tried it in this way:
element.datetimepicker({
timepicker:false,
format:'Y-m-d',
formatDate:'Y-m-d',
closeOnDateSelect: true,
onChangeDateTime: function(dp, $input){
var val = $input['context']['value'];
ctrl.$setViewValue(val);
ctrl.$render();
scope.$apply();
}
// minDate:'-1970/01/02', // yesterday is minimum date
//maxDate:'+1970/01/02' // and tommorow is maximum date calendar
});
Upvotes: 0
Reputation: 2177
To complete cdmckay's solution so the datetime picker widget is correctly initialized with the ng-model's value, I've added a listener on dp.show
event. So, my solution is :
'use strict';
angular.module('frontStreetApp.directives', [])
.directive('psDatetimePicker', function (moment) {
var format = 'MM/DD/YYYY hh:mm A';
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attributes, ctrl) {
element.datetimepicker({
format: format
});
var picker = element.data("DateTimePicker");
ctrl.$formatters.push(function (value) {
var date = moment(value);
if (date.isValid()) {
return date.format(format);
}
return '';
});
/**
* Update datetime picker's value from ng-model when opening the datetime picker's dropdown
*/
element.on('dp.show', function() {
picker.setDate(ctrl.$viewValue);
});
/**
* Update ng-model when datetime picker's value changes
*/
element.on('change', function (event) {
scope.$apply(function () {
var date = picker.getDate();
ctrl.$setViewValue(date);
});
});
}
};
});
Upvotes: 0
Reputation: 32240
I had the same issue. Here's what I ended up doing that worked well for me:
'use strict';
angular.module('frontStreetApp.directives')
.directive('psDatetimePicker', function (moment) {
var format = 'MM/DD/YYYY hh:mm A';
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attributes, ctrl) {
element.datetimepicker({
format: format
});
var picker = element.data("DateTimePicker");
ctrl.$formatters.push(function (value) {
var date = moment(value);
if (date.isValid()) {
return date.format(format);
}
return '';
});
element.on('change', function (event) {
scope.$apply(function() {
var date = picker.getDate();
ctrl.$setViewValue(date.valueOf());
});
});
}
};
});
Here's the HTML:
<!-- The dueDate field is a UNIX offset of the date -->
<input type="text"
ng-model="dueDate"
ps-datetime-picker
class="form-control">
You can check out the gists and a bit more information in my blog post.
Upvotes: 5
Reputation: 31
A possible solution to problem 1:
You will need to set the view value to the date that is in the model right from the beginning, by doing something like this:
if (ngModel.$viewValue) {
picker.data("DateTimePicker").setDate(new Date(ngModel.$modelValue));'
picker.on('change', function(e) {
....
});
}
Upvotes: 0
Reputation:
Can help you with the first point; add $watch to your element inside the link function and set
value="{{$scope.variableWithTheInitialDate}}"in the text field; that way when loaded, the datetimepicker will be set already.
Upvotes: 0