Rolando
Rolando

Reputation: 62674

How to detect bootstrap datetimepicker change events within angularJS

I am using this datetime picker in angular.

https://eonasdan.github.io/bootstrap-datetimepicker/

Inside of a controller I have:

$('#picker').datetimepicker();

In my HTML I have:

    <div id="#picker" >
        <input type='text' style="font-size:10pt;" class="rptv-input" placeholder="Start Time" ng-model='adate' ng-change="datechange()" />
        <span class="input-group-addon">
            <span class="glyphicon glyphicon-calendar"></span>
        </span>
    </div>       

Everything is managed by a controller "AppController". The problem is, when I select a date on the calendar by click, it does not trigger any "change" event (in other words, datechange is not fired). If I do a watch on ng-model "adate", it also does not seem to trigger it. If I type in the text box, then the scope variable changes.

How can I detect changes on the text box if the user clicks on a date in the selector to change it?

Upvotes: 5

Views: 9922

Answers (6)

Luis Noguera
Luis Noguera

Reputation: 1

I recently was having the same issue (detecting the datetimepicker change event within angularJS using the https://eonasdan.github.io/bootstrap-datetimepicker/) and what actually worked for me was the same idea of @Mark Pieszak but with some minor changes (I guess because of the pluging version). If you are using the version 4 of the pluging you need to use the on listener and the dp.change event to get the change event

   app.directive('datetimepicker', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            element.datetimepicker({
                defaultDate: new Date(),
                maxDate: moment().endOf('d'),
                locale: 'es',
                format: 'DD/MM/YYYY',
                widgetPositioning: { horizontal: 'right', vertical: 'bottom' }

            }).on('dp.change', function (date) {
                scope.$apply(function () {
                    ngModelCtrl.$setViewValue(date.date !== false ? date : null);
                });
            });
        }
    }
});

and the usage is the same.

<input datetimepicker name="yourInputName" id="yourInputName" type='text' class="form-control" ng-model="yourModelProperty" ng-required="yourRequiredExpression" ng-disabled="yourDisabledExpression" />

The expression i used as the ngModelCtrl.$setViewValue() parameter was because when i only passed the date parameter the input was getting valid even when i left it blank.

Upvotes: 0

pistol-pete
pistol-pete

Reputation: 1261

For a global solution for your app, I definitely support the custom directives as shown in the answers above. But for one-offs:

According to the current Bootstrap documentation, the attached event handler needs to be looking for the changeDate event. See example:

Html Markup:

<input class="date-picker" type="text" ng-model="$ctrl.model.date" />

Angular 1.5.9 Controller:

(function () {

    'use strict'

    var MyTestController = function () {

        var vm = this;

        vm.model = {
            date: undefined
        };

        (function initilize() {
            $('.date-picker').datepicker({
                autoclose: true,
                forceParse: false
            }).on('changeDate', function (e) {
                console.log('date was changed to: ' + e.date);
            });
        })();
    };

    angular.module('myApp').component('myTest', {
        controller: [MyTestController],
        templateUrl: 'app/modules/my-test/view.html'
    });
})();

Upvotes: 1

Fluffy
Fluffy

Reputation: 217

Another option would be

HTML:

        <div class="input-group date" id="dateTimePicker">
            <input type="text" class="form-control" />
            <span class="input-group-addon" ng-click="setDateTime()">
                <span class="glyphicon glyphicon-calendar"></span>
            </span>
        </div>

Controller:

        $scope.setDateTime = function () { 

            $("#dateTimePicker").datetimepicker().on("dp.change", function (data) {

                $scope.date = data.date._d;

            });

        }

Upvotes: 3

akfa_ru
akfa_ru

Reputation: 1

My directive and angular 1.5 component

        .directive('externalUpdate', ['$parse', function($parse) {
            return {
                link: function(scope, element, attrs){
                    var setterFunc = $parse(attrs.ngModel).assign;
                    var func = scope.$eval(attrs.externalUpdate);
                    func(element, function(value) {
                        scope.$apply(function() {
                            setterFunc(scope, value);
                        });
                    });
                }
            };
        }])
        .component('datebox', {
            bindings: {
                size: '@@?',
                name: '@@',
                text: '@@',
                model: '=',
                classes: '@@?',
                disabled: '=?',
                time: '<?'
            },
            controller: function() {
                if (!this.size) {
                    this.col1 = '6';
                    this.col2 = '6';
                } else {
                    var size = parseInt(this.size);
                    this.col1 = size.toString();
                    this.col2 = (12 - size).toString();
                }
                this.updateInput = function(element, setter) {
                    element.on("dp.change", function() { setter(element.val()); });
                }
            },
            template: String.raw`
                <div class="form-group">
                    <label ng-if="$ctrl.col1!='0'" for="{{::$ctrl.name}}" class="col-md-{{::$ctrl.col1}} control-label">{{::$ctrl.text}}</label>
                    <div class="col-md-{{::$ctrl.col2}}">
                        <input type="text" id="{{::$ctrl.name}}" ng-disabled="$ctrl.disabled"
                            class="form-control input-sm {{::$ctrl.classes}}" 
                            ng-class="[{datepicker: $ctrl.time!=true},{datetimepicker: $ctrl.time==true}]" 
                            ng-model="$ctrl.model" external-update="$ctrl.updateInput">
                    </div>
                </div>`
        })
        .component('daterangebox', {
            bindings: {
                size: '@@?',
                name: '@@',
                text: '@@',
                model: '=',
                classes: '@@?',
                disabled: '=?',
                time: '<?'
            },
            controller: function() {
                if (!this.model || typeof this.model !== 'object') {
                    this.model = {};
                }
                if (!this.size) {
                    this.col1 = '6';
                    this.col2 = '6';
                } else {
                    var size = parseInt(this.size);
                    this.col1 = size.toString();
                    this.col2 = (12 - size).toString();
                }
                this.updateInput = function(element, setter) {
                    element.on("dp.change", function() { setter(element.val()); });
                }
            },
            template: String.raw`
                <div class="form-group">
                    <label ng-if="$ctrl.col1!='0'" for="{{::$ctrl.name}}" class="col-md-{{::$ctrl.col1}} control-label">{{::$ctrl.text}}</label>
                    <div class="col-md-{{::$ctrl.col2}}">
                        <div class="input-group">
                            <input type="text" id="{{::$ctrl.name}}" ng-disabled="$ctrl.disabled"
                                class="form-control input-sm {{::$ctrl.classes}}"
                                ng-class="[{datepicker: $ctrl.time!=true},{datetimepicker: $ctrl.time==true}]"
                                ng-model="$ctrl.model.start" external-update="$ctrl.updateInput">
                            <span class="input-group-addon input-sm">-</span>
                            <input type="text" ng-disabled="$ctrl.disabled"
                                class="form-control input-sm {{::$ctrl.classes}}" 
                                ng-class="[{datepicker: $ctrl.time!=true},{datetimepicker: $ctrl.time==true}]" 
                                ng-model="$ctrl.model.end" external-update="$ctrl.updateInput">
                        </div>
                    </div>
                </div>`
        })

Upvotes: 0

Hosni
Hosni

Reputation: 668

The change event is triggered within bootstrap, so you might need to create a custom directive for your timepicker in order to catch the change event like so :

  .directive('yourDirective', function(){
                        return{
                          require: '?ngModel',
                          restrict: 'A',
                          link: function (scope,element,attrs, ngModel){
                            if (!ngModel) return;
                            element.bind('change', function(e){
                           //triggered event if change
                            });
                          }
                        };
                      });

Upvotes: 1

Mark Pieszak - Trilon.io
Mark Pieszak - Trilon.io

Reputation: 67131

Here's the jist of the logic, but basically you need to make a directive that in turn creates the datetimepicker itself. Then within boostraps change() function you must do a $apply() which triggers a digest cycle and updates your model.

boostrap 3 datetimepicker event documentation

angular.module('yourApp')
.directive('datetimepicker', function () {
    return {
        restrict: 'A',
        require : 'ngModel',
        link : function (scope, element, attrs, ngModelCtrl) {

            element.datetimepicker({
                change:function (date) {

                    // Triggers a digest to update your model
                    scope.$apply(function () {
                        ngModelCtrl.$setViewValue(date);
                    });

                }
            });
        }
    } 
});

Useage :

<input datetimepicker ng-model="adate" />

Upvotes: 4

Related Questions