{"id":11992,"date":"2025-02-10T05:05:23","date_gmt":"2025-02-10T05:05:23","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=11992"},"modified":"2025-02-10T05:05:23","modified_gmt":"2025-02-10T05:05:23","slug":"wrap-datetimepicker-js-into-angularjs-directive","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/angular\/wrap-datetimepicker-js-into-angularjs-directive","title":{"rendered":"How to Wrap the Datetimepicker JS into AngularJS Directive"},"content":{"rendered":"<p>The user aims to incorporate datetimepicker.js, a jQuery plugin, into an AngularJS application by developing a custom directive. The primary obstacle is that the datetimepicker does not seamlessly bind to AngularJS&#8217;s ng-model, which disrupts the two-way data binding mechanism. As a result, there are inconsistencies between the Angular model and the date chosen through the datetimepicker.<\/p>\n<h2>Problem Overview<\/h2>\n<ol>\n<li>If the date is set via json before rendered in view, the initial date did not display, I can not see any log of the execution of ngModel render method.<\/li>\n<li>When I picked a date, it got a string based datetime to the json data, not a long format. And in other related fragment in the view, the string based date can not parsed by angular date filter.<\/li>\n<li>When used it in modal dialog, it&#8217;s value is not cleared when the modal window is popup in the next time.<\/li>\n<\/ol>\n<p>Here in example we have Eonasdan\/bootstrap-datetimepicker<\/p>\n<p>The <strong>Eonasdan\/bootstrap-datetimepicker<\/strong> library is a jQuery-based datetime picker and does not natively support Angular. To integrate it with Angular, you would need to use a wrapper or directive that bridges the gap between jQuery and Angular.<\/p>\n<p>So here we have given solution for all angular versions<\/p>\n<h2>Angular JS<\/h2>\n<h3>Directive<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\napp.directive('datetimepicker', function() {\r\n    return {\r\n        restrict: 'A',\r\n        require: 'ngModel',\r\n        link: function(scope, element, attrs, ngModelCtrl) {\r\n            console.log('call datetimepicker link...');\r\n            var picker = element.datetimepicker({\r\n                dateFormat: 'dd\/MM\/yyyy hh:mm:ss',\r\n                useCurrent: false \/\/ Prevent default date setting on initialization\r\n            });\r\n\r\n            \/\/ Model -&gt; View (render)\r\n            ngModelCtrl.$render = function() {\r\n                console.log('ngModelCtrl.$viewValue@' + ngModelCtrl.$viewValue);\r\n                picker.setDate(ngModelCtrl.$viewValue || '');\r\n            };\r\n\r\n            \/\/ View -&gt; Model (change event)\r\n            picker.on('dp.change', function(e) {\r\n                console.log('dp.change ' + e.date);              \r\n                scope.$apply(function(){\r\n                    \/\/ Store Date object in the model\r\n                    ngModelCtrl.$setViewValue(e.date ? e.date.toISOString() : null); \/\/ Using ISO string format\r\n                });\r\n            });\r\n\r\n            \/\/ Optional: Clear date when modal is opened again\r\n            scope.$on('modalOpened', function() {\r\n                ngModelCtrl.$setViewValue(null); \/\/ Reset the date on modal open\r\n                picker.clear();\r\n            });\r\n\r\n            \/\/ Optional: Listen for modal closing event and clear date if needed\r\n            scope.$on('modalClosed', function() {\r\n                picker.clear();\r\n            });\r\n        }\r\n    };\r\n});\r\n<\/pre>\n<h3>Html<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\n<div class=\"container\">\r\n      <h2>Datetime Picker Example<\/h2>\r\n      <form>\r\n        <form>\r\n          <div class=\"col-md-6\">\r\n            <div class=\"input-group date\" id=\"endTime\" data-datetimepicker ng-model=\"stat.endTime\" data-date-format=\"MM\/DD\/YYYY hh:mm A\/PM\">\r\n              <input class=\"form-control\" type=\"text\" placeholder=\"End\"\/>\r\n              <span class=\"input-group-addon\">\r\n                <span class=\"glyphicon glyphicon-calendar\"><\/span>\r\n              <\/span>\r\n            <\/div>\r\n          <\/div>\r\n        <\/form>\r\n      <\/form>\r\n    <\/div>\r\n\r\nController\r\n\r\napp.controller('MainCtrl', [\r\n        '$scope',\r\n        function ($scope) {\r\n          $scope.dueDate = new Date();\r\n        },\r\n      ]);\r\n\r\n<\/pre>\n<p><em>For Angular 2+ versions <\/em>, need to install packages using below command<br \/>\nnpm install jquery moment <strong>eonasdan-bootstrap-datetimepicker<\/strong><\/p>\n<p>Need to add styles and scripts in angular.json file<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\n\"styles\": [\r\n  \"node_modules\/bootstrap\/dist\/css\/bootstrap.min.css\",\r\n  \"node_modules\/eonasdan-bootstrap-datetimepicker\/build\/css\/bootstrap-datetimepicker.min.css\"\r\n],\r\n\"scripts\": [\r\n  \"node_modules\/jquery\/dist\/jquery.min.js\",\r\n  \"node_modules\/moment\/min\/moment.min.js\",\r\n  \"node_modules\/eonasdan-bootstrap-datetimepicker\/build\/js\/bootstrap-datetimepicker.min.js\"\r\n]\r\n<\/pre>\n<h2>Angular 18<\/h2>\n<p><strong>datetime-picker.directive.ts<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\nimport $ from 'jquery';\r\nimport { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular\/core';\r\nimport 'eonasdan-bootstrap-datetimepicker';\r\n\r\n@Directive({\r\n  selector: '[appDatetimepicker]',\r\n  standalone: true\r\n})\r\nexport class DatetimepickerDirective implements OnInit, OnDestroy {\r\n  @Input() options: any;\r\n  @Output() dateChange = new EventEmitter<string>();\r\n\r\n  private picker: any;\r\n  constructor(private el: ElementRef) {}\r\n  ngOnInit(): void {\r\n    \/\/ Use type assertion to avoid TypeScript error\r\n    this.picker = (<any>$(this.el.nativeElement)).datetimepicker(this.options);\r\n\r\n    this.picker.on('dp.change', (e: any) => {\r\n      this.dateChange.emit(e.date ? e.date.format('YYYY-MM-DD HH:mm') : '');\r\n    });\r\n  }\r\n  ngOnDestroy(): void {\r\n    if (this.picker) {\r\n      (<any>this.picker.data('DateTimePicker')).destroy();\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p><strong>App.component.html<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\n<div class=\"form-group\">\r\n  <label for=\"datetime\">Select Date and Time:<\/label>\r\n  <input\r\n    id=\"datetime\"\r\n    type=\"text\"\r\n    class=\"form-control\"\r\n    appDatetimepicker\r\n    [options]=\"datetimeOptions\"\r\n    (dateChange)=\"onDateChange($event)\"\r\n  \/>\r\n<\/div>\r\n<\/pre>\n<p><b>app.component.ts<\/b><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\nimport { Component } from '@angular\/core';\r\nimport { DatetimepickerDirective } from '.\/datetime-picker\/datetime-picker.directive';\r\n\r\n@Component({\r\n  selector: 'app-root',\r\n  templateUrl: '.\/app.component.html',\r\n  standalone : true,\r\n  imports: [DatetimepickerDirective],\r\n})\r\nexport class AppComponent {\r\n  datetimeOptions = {\r\n    format: 'YYYY-MM-DD HH:mm',\r\n    sideBySide: true,\r\n    showTodayButton: true\r\n  };\r\n  onDateChange(date: string): void {\r\n    console.log('Selected date:', date);\r\n  }\r\n}\r\n<\/pre>\n<h2>Angular 2 to 17(module based)<\/h2>\n<p><strong>No Standalone Components:<\/strong> Angular 2 to 17 doesn&#8217;t support the standalone: true flag in components or directives, so the directive needs to be declared in the module.<br \/>\n<strong>Module-based Declaration:<\/strong> The directive will be declared within an Angular module (@NgModule) instead of being standalone.<\/p>\n<p><strong>datetime-picker.directive.ts<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\r\nimport * as $ from 'jquery';\r\nimport { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular\/core';\r\nimport 'eonasdan-bootstrap-datetimepicker';\r\n\r\n@Directive({\r\n  selector: '[appDatetimepicker]'\r\n})\r\nexport class DatetimepickerDirective implements OnInit, OnDestroy {\r\n  @Input() options: any;\r\n  @Output() dateChange = new EventEmitter<string>();\r\n\r\n  private picker: any;\r\n  constructor(private el: ElementRef) {}\r\n  ngOnInit(): void {\r\n    this.picker = $(this.el.nativeElement).datetimepicker(this.options);\r\n    this.picker.on('dp.change', (e: any) => {\r\n      this.dateChange.emit(e.date ? e.date.format('YYYY-MM-DD HH:mm') : '');\r\n    });\r\n  }\r\n  ngOnDestroy(): void {\r\n    if (this.picker) {\r\n      this.picker.data('DateTimePicker').destroy();\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p>Now need to import <strong>DatetimepickerDirective<\/strong> in the app module\u2019s import array. Other than this everything is the same as angular 18.<\/p>\n<p>In summary, while <strong>Eonasdan\/bootstrap-datetimepicker<\/strong> itself is not natively compatible with Angular, there are Angular-specific wrappers and alternative libraries available that can provide similar functionality.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The user aims to incorporate datetimepicker.js, a jQuery plugin, into an AngularJS application by developing a custom directive. The primary obstacle is that the datetimepicker does not seamlessly bind to AngularJS&#8217;s ng-model, which disrupts the two-way data binding mechanism. As a result, there are inconsistencies between the Angular model and the date chosen through the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":11993,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[9],"tags":[],"class_list":["post-11992","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/11992"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=11992"}],"version-history":[{"count":1,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/11992\/revisions"}],"predecessor-version":[{"id":11994,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/11992\/revisions\/11994"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/11993"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=11992"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=11992"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=11992"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}