2

I'm trying to use knockoutjs with the tempusdominus datetimepicker and am having a problem with the custom binding. The binding will work during initialization, but changes made through the datepicker ui or not resulting in update events being handled by the custom binding. Please ignore the missing icons, I don't think that is what is causing the problem.

ko.bindingHandlers.datepicker = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    //initialize datepicker with some optional options
    var options = allBindings().datepickerOptions || {
      format: 'MM/DD/YYYY HH:mm',
      defaultDate: valueAccessor()()
    };
    $(element).datetimepicker(options);

    //when a user changes the date, update the view model
    ko.utils.registerEventHandler(element, "change.datetimepicker", function(event) {
      var value = valueAccessor();
      if (ko.isObservable(value)) {
        value(event.date);
      }
      console.log("change.datetimepicker"); //, event.date.format());
    });
  },
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var val = ko.utils.unwrapObservable(valueAccessor());
    if ($(element).datetimepicker) {
      $(element).datetimepicker("date", val);
    }
    console.log("update called"); //, $(element).datetimepicker("date").format());
  }
};

var viewModel = {};

function QuoteViewModel(data) {
  var self = this;
  self.dt2 = ko.observable(moment());
};

viewModel = new QuoteViewModel();
ko.applyBindings(viewModel);

/*
$("#datetimepicker1").on("change.datetimepicker", function (e) {
  console.log("on change", e.date.format());
});
*/
<link href="https://cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/d004434a5ff76e7b97c8b07c01f34ca69e635d97/build/css/bootstrap-datetimepicker.css" rel="stylesheet"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.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/moment.js/2.9.0/moment-with-locales.js"></script>
<script src="https://cdn.rawgit.com/Eonasdan/bootstrap-datetimepicker/d004434a5ff76e7b97c8b07c01f34ca69e635d97/src/js/bootstrap-datetimepicker.js"></script>

<div class="col-sm-6">
  <div class="form-group">
    <div class="input-group date" id="datetimepicker1" data-target-input="nearest">
      <input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" data-bind="datepicker: dt2" />
      <div class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
        <div class="input-group-text"><i class="fa fa-calendar"></i></div>
      </div>
    </div>
  </div>
</div>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre> SCRIPT

https://jsfiddle.net/mh_surge/7yrze0Lv/6/

If I uncomment the code at the bottom that adds the non-ko change event handler, it always fires as expected. This leads me to believe that there might be a problem with ko.utils.registerEventHandler, but the source code for that function seems fine.

Why is the update and change event only being fired off only once during init and how do I fix it?

Tomalak
  • 332,285
  • 67
  • 532
  • 628
Mark
  • 595
  • 5
  • 13

3 Answers3

3

The change event that is triggered does not contain an event.date property. The reason your commented out jQuery version does, is that it targets a different element.

In the knockout code, you're using the element argument passed to init and update. This refers to the element that has the datepicker data-bind.

In the jQuery code, you're targeting $("#datetimepicker1"), which is a <div> that has a custom change element implemented by the plugin.

To fix this, you'll need to:

  • Put the datepicker binding on the element with id datetimepicker1 (i.e. not on the input element)
  • Use the $(element).on("change.datepicker", ...) syntax in the init function
  • Add an element disposal callback to clean up the widget instance in case knockout removes the element from the DOM
user3297291
  • 22,592
  • 4
  • 29
  • 45
0

The following edit works for me with both the calendar popup AND manual editing of the text input.

ko.bindingHandlers.dateTimePicker = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var options = allBindingsAccessor().dateTimePickerOptions || {};
        var initialValue = ko.utils.unwrapObservable(valueAccessor());
        if (initialValue) {
            options.date = initialValue;
        }
        $(element).datetimepicker(options);

        ko.utils.registerEventHandler(element, "change.datetimepicker", function (event) {
            var value = valueAccessor();
            if (ko.isObservable(value)) {
                value(event.date || event.target.value || null);
            }
        });

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).datetimepicker("destroy");
        });
    },
    update: function (element, valueAccessor) {
        var val = ko.utils.unwrapObservable(valueAccessor());
        console.log(arguments, val);
        if ($(element).datetimepicker) {
            $(element).datetimepicker("date", val);
        }
    }
};
m-albert
  • 1,089
  • 8
  • 15
0

I was getting similar adding valueUpdate:'input' e.g

      <input type="text" autocomplete="off" data-bind="value:addStart,valueUpdate:'input'" class="form-control datetimepicker-input timepicker" id="addStartTime" data-toggle="datetimepicker" data-target="#addStartTime" />
           

Seems to solve the issue.

Richard Housham
  • 1,525
  • 2
  • 15
  • 31