jQuery UI datepicker change event not caught by Kn

2018-12-31 18:30发布

I'm trying to use KnockoutJS with jQuery UI. I have an input element with a datepicker attached. I'm currently running knockout.debug.1.2.1.js and it seems that the change event is never being caught by Knockout. The element looks like this:

<input type="text" class="date" data-bind="value: RedemptionExpiration"/>

I've even tried changing the valueUpdate event type but to no avail. It seems like Chrome causes a focus event just before it changes the value, but IE doesn't.

Is there some Knockout method that "rebinds all the bindings"? I technically only need the value changed before I send it back to the server. So I could live with that kind of workaround.

I think the problem's the datepicker's fault, but I can't figure out how to fix this.

Any ideas?

13条回答
十年一品温如言
2楼-- · 2018-12-31 18:56

I think that for the jQuery UI datepicker it is preferable to use a custom binding that will read/write with Date objects using the APIs provided by the datepicker.

The binding might look like (from my answer here):

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

        $el.datepicker(options);

        //handle the field changing by registering datepicker's changeDate event
        ko.utils.registerEventHandler(element, "changeDate", function () {
            var observable = valueAccessor();
            observable($el.datepicker("getDate"));
        });

        //handle disposal (if KO removes by the template binding)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
            $el.datepicker("destroy");
        });

    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor()),
            $el = $(element);

        //handle date data coming via json from Microsoft
        if (String(value).indexOf('/Date(') == 0) {
            value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
        }

        var current = $el.datepicker("getDate");

        if (value - current !== 0) {
            $el.datepicker("setDate", value);
        }
    }
};

You would use it like:

<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />

Sample in jsFiddle here: http://jsfiddle.net/rniemeyer/NAgNV/

查看更多
时光乱了年华
3楼-- · 2018-12-31 19:06

Using custom bindings provided in previous answers is not always possible. Calling $(element).datepicker(...) takes some considerable time, and if you have, for example, a few dozens, or even hundreds of elements to call this method with, you have to do it "lazy", i.e. on demand.

For example, the view model may be initialized, the inputs being inserted into the DOM, but the corresponding datepickers will only be initialized when a user clicks them.

So, here is my solution:

Add a custom binding that allows to attach arbitrary data to a node:

KO.bindingHandlers.boundData = {
  init: function(element, __, allBindings) {
    element.boundData = allBindings.get('boundData');
  }
};

Use the binding to attcah the observable used for the input's value:

<input type='text' class='my-date-input'
       data-bind='textInput: myObservable, boundData: myObservable' />

And finally, when initializing the datepicker, use it's onSelect option:

$('.my-date-input').datepicker({
  onSelect: function(dateText) {
    this.myObservable(dateText);
  }
  //Other options
});

This way, every time a user changes the date with the datepicker, the corresponding Knockout observable is also updated.

查看更多
步步皆殇っ
4楼-- · 2018-12-31 19:09

I solved this problem by changing the order of my included script files:

<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
<script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
查看更多
十年一品温如言
5楼-- · 2018-12-31 19:09

I think it can be done much easier: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

So you do not need manual change handling in init function.

But in this case, your 'myDate' variable will get only visible value, not Date object.

查看更多
不流泪的眼
6楼-- · 2018-12-31 19:11

Alternatively, you can specify this in binding:

Update:

 function (element, valueAccessor) {
    var value = ko.utils.unwrapObservable(valueAccessor()),
        current = $(element).datepicker("getDate");

    if (typeof value === "string") {            
       var dateValue = new Date(value);
       if (dateValue - current !== 0)
           $(element).datepicker("setDate", dateValue);
    }               
}
查看更多
后来的你喜欢了谁
7楼-- · 2018-12-31 19:17

Thanks for this article I found it very useful.

If you want the DatePicker to behave exactly like the JQuery UI default behaviour I recommend adding a blur on the element in the change event handler:

i.e.

    //handle the field changing
    ko.utils.registerEventHandler(element, "change", function () {
        var observable = valueAccessor();
        observable($(element).datepicker("getDate"));

        $(element).blur();

    });
查看更多
登录 后发表回答