1

I need to attach a bootstrap datepicker to two input fields that also need a value binding since I need to be able to dynamically set the value of the input according to changes in my observable.

So far, the binding works only one way: When I pick a date in the datepicker, the observable is correctly updated. But when I change the value of the attached observable in my viewmodel, the input does not reflect the change.

This is my binding handler:

ko.bindingHandlers.bootstrapDP = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).bootstrapDP(options).on("changeDate", function (ev) {
            var observable = valueAccessor();
            observable($(element).val());
        });
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        $(element).bootstrapDP("setValue", value);
    }
};

In my viewmodel I have an object encapsulating startDate and endDate:

self.dateFilter = {
    startDate: ko.observable(),
    endDate: ko.observable()
};

This is my HTML:

<input type="text" data-bind="bootstrapDP: dateFilter.startDate, value: dateFilter.startDate" />
<input type="text" data-bind="bootstrapDP: dateFilter.endDate, value: dateFilter.endDate" />

I'm using this datepicker library (in noConflict()-mode): https://github.com/eternicode/bootstrap-datepicker

DOCS here: http://bootstrap-datepicker.readthedocs.org/en/stable/

What will I need to add/change/do differently to get the desired result?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
connexo
  • 53,704
  • 14
  • 91
  • 128

2 Answers2

3

I believe you want to change this line:

$(element).bootstrapDP("setValue", value);

To:

$(element).bootstrapDP("update", value);

This was taken from the bootstrap datepicker documentation.

The snippet below demonstrates this working.

$.fn.bootstrapDP = $.fn.datepicker;

ko.bindingHandlers.bootstrapDP = {
  init: function(element, valueAccessor, allBindingsAccessor) {
    var options = allBindingsAccessor().datepickerOptions || {};
    $(element).bootstrapDP(options).on("changeDate", function(ev) {
      var observable = valueAccessor();
      observable($(element).val());
    });
  },
  update: function(element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor());
    // use "update" instead of "setValue"
    $(element).bootstrapDP("update", value);
  }
};

var vm = {
  startDate: ko.observable(),
  endDate: ko.observable()
};
ko.applyBindings(vm);

setTimeout(function(){
  vm.startDate(new Date())
}, 1000);
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.4.0/css/bootstrap-datepicker3.standalone.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.4.0/js/bootstrap-datepicker.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<input type="text" data-bind="bootstrapDP: startDate, value: startDate" />
<input type="text" data-bind="bootstrapDP: endDate, value: endDate" />
Anish Patel
  • 4,332
  • 1
  • 30
  • 45
  • Yes, this also solves another problem I had: The datepicker wasn't properly initialized with my initial values from the observables. I had to go a step further and even remove the `changeDate` function completely. The value binding fully does the trick already. – connexo Jul 23 '15 at 12:30
  • @connexo glad I could help buddy – Anish Patel Jul 23 '15 at 12:31
0

So this is the working code I ended up with, just for those who need a quick working solution and don't want to work through the comments:

Custom binding handler:

ko.bindingHandlers.bootstrapDP = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var options = allBindingsAccessor().datepickerOptions || {};
        $(element).bootstrapDP(options);
    },
    update: function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        $(element).bootstrapDP("update", value);
    }
};

Observables for the dates in the viewmodel:

self.dateFilter = {
    startDate: ko.observable(),
    endDate: ko.observable()
};

The HTML with the bindings:

<input type="text" data-bind="bootstrapDP: dateFilter.startDate, value: dateFilter.startDate" />
<input type="text" data-bind="bootstrapDP: dateFilter.endDate, value: dateFilter.endDate" />
connexo
  • 53,704
  • 14
  • 91
  • 128