Bingla
Bingla

Reputation: 547

Knockout bindingHandler with Eonasdan datepicker does not fire dp.change event when selecting date

I'm using Knockout(ver 3.4.2), moment(ver 2.29.1) and Eonasdan datepicker (ver 4.17.47) plugin för selecting dates.

My problem is that the dp.change event is not fired when the user picks a date. It fires when the datepicker/widget is opened, ie the user clicks the calendar-icon.

The result is that the first date the user picks get's ignored (event fires before date is actually picked) and the date is only updated if the user opens the datepicker again and the event fires.

I have been using the knockout bindinghandle example from eonasdans installation page: http://eonasdan.github.io/bootstrap-datetimepicker/Installing/

ko.bindingHandlers.datepicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        //initialize datepicker with some optional options
        var options = allBindingsAccessor().datepickerOptions|| {};
        $(element).datetimepicker(options);

        //when a user changes the date, update the view model
        ko.utils.registerEventHandler(element, "dp.change", function (event) {
            var value = valueAccessor();
            if (ko.isObservable(value)) {
                if (event.date != null && !(event.date instanceof Date)) {
                    value(event.date.toDate());
                } else {
                    value(event.date);
                }
            }
        });

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            var picker = $(element).data("DateTimePicker");
            if (picker) {
                picker.destroy();
            }
        });
    },
    update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {

        var picker = $(element).data("DateTimePicker");
        //when the view model is updated, update the widget
        if (picker) {
            var koDate = ko.utils.unwrapObservable(valueAccessor());

            //in case return from server datetime i am get in this form for example /Date(93989393)/ then fomat this
            //koDate = (typeof (koDate) !== 'object') ? new Date(parseFloat(koDate.replace(/[^0-9]/g, ''))) : koDate;

            picker.date(koDate);
        }
    }
};

And my knockout view looks like this:

var exportLicenserModel = function () {
        var self = this;
        self.Startdatum = ko.observable((new moment()).month(0).date(1));
}

And the HTML is:

<div class='input-group js-date'>
  <input type="text" class="form-control" id="licenseStartDate" data-bind="datepicker: Startdatum, datepickerOptions: { locale: 'sv', format: 'YYYY-MM-DD', useCurrent: false }" />
  <span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
</div>

Upvotes: 0

Views: 334

Answers (2)

Bingla
Bingla

Reputation: 547

I found the reason why it seemed the dp.change event didn't fire.

There was a old and obsolete jQuery class in the project that looked up marked datepickers and activated them, making them bind twice.

$('.js-date').datetimepicker({ locale: 'sv', format: 'YYYY-MM-DD', useCurrent: false });

After removing this, everything worked as expected.

Upvotes: 0

Philip Bijker
Philip Bijker

Reputation: 5115

Seems to work just fine. I added a console.log statement to the dp.change handler and also added a text-binding to show the current value for Startdatum. The handler gets called and the value is updated. Have a look at the snippet below:

ko.bindingHandlers.datepicker = {
  init: function(element, valueAccessor, allBindingsAccessor) {
    //initialize datepicker with some optional options
    var options = allBindingsAccessor().datepickerOptions || {};
    $(element).datetimepicker(options);

    //when a user changes the date, update the view model
    ko.utils.registerEventHandler(element, "dp.change", function(event) {
      var value = valueAccessor();
      if (ko.isObservable(value)) {
        if (event.date != null && !(event.date instanceof Date)) {
          value(event.date.toDate());
        } else {
          value(event.date);
        }
      }
      console.log(`Changed to ${value()}`);
    });

    ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
      var picker = $(element).data("DateTimePicker");
      if (picker) {
        picker.destroy();
      }
    });
  },
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {

    var picker = $(element).data("DateTimePicker");
    //when the view model is updated, update the widget
    if (picker) {
      var koDate = ko.utils.unwrapObservable(valueAccessor());

      //in case return from server datetime i am get in this form for example /Date(93989393)/ then fomat this
      //koDate = (typeof (koDate) !== 'object') ? new Date(parseFloat(koDate.replace(/[^0-9]/g, ''))) : koDate;

      picker.date(koDate);
    }
  }
};

var exportLicenserModel = function() {
  var self = this;
  self.Startdatum = ko.observable((new moment()).month(0).date(1));
}

ko.applyBindings(new exportLicenserModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>

<div class='input-group js-date' style="position: relative">
  <input type="text" class="form-control" id="licenseStartDate" data-bind="datepicker: Startdatum, datepickerOptions: { locale: 'sv', format: 'YYYY-MM-DD', useCurrent: false }" />
  <span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
</div>
<p>StartDatum: <strong data-bind="text: Startdatum"></strong></p>

Upvotes: 1

Related Questions