jQuery UI datepicker change event not caught by KnockoutJS

后端 未结 13 2140
长发绾君心
长发绾君心 2020-11-22 16:16

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 th

相关标签:
13条回答
  • 2020-11-22 16:16

    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.

    0 讨论(0)
  • 2020-11-22 16:17

    Same as RP Niemeyer, but better support of WCF DateTime, Timezones and Using the DatePicker onSelect JQuery property.

            ko.bindingHandlers.datepicker = {
            init: function (element, valueAccessor, allBindingsAccessor) {
                //initialize datepicker with some optional options
                var options = allBindingsAccessor().datepickerOptions || {};
    
                var funcOnSelectdate = function () {
                    var observable = valueAccessor();
                    var d = $(element).datepicker("getDate");
                    var timeInTicks = d.getTime() + (-1 * (d.getTimezoneOffset() * 60 * 1000));
    
                    observable("/Date(" + timeInTicks + ")/");
                }
                options.onSelect = funcOnSelectdate;
    
                $(element).datepicker(options);
    
                //handle the field changing
                ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
    
                //handle disposal (if KO removes by the template binding)
                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(element).datepicker("destroy");
                });
    
            },
            update: function (element, valueAccessor) {
                var value = ko.utils.unwrapObservable(valueAccessor());
    
                //handle date data coming via json from Microsoft
                if (String(value).indexOf('/Date(') == 0) {
                    value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
                }
    
                current = $(element).datepicker("getDate");
    
                if (value - current !== 0) {
                    $(element).datepicker("setDate", value);
                }
            }
        };
    

    Enjoy :)

    http://jsfiddle.net/yechezkelbr/nUdYH/

    0 讨论(0)
  • 2020-11-22 16:23

    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/

    0 讨论(0)
  • 2020-11-22 16:24

    Here is a version of RP Niemeyer's answer that will work with the knockout validation scripts found here: http://github.com/ericmbarnard/Knockout-Validation

    ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {};
            $(element).datepicker(options);
    
            //handle the field changing
            ko.utils.registerEventHandler(element, "change", function () {
                var observable = valueAccessor();
                observable($(element).val());
                if (observable.isValid()) {
                    observable($(element).datepicker("getDate"));
    
                    $(element).blur();
                }
            });
    
            //handle disposal (if KO removes by the template binding)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                $(element).datepicker("destroy");
            });
    
            ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor);
    
        },
        update: function (element, valueAccessor) {
            var value = ko.utils.unwrapObservable(valueAccessor());
    
            //handle date data coming via json from Microsoft
            if (String(value).indexOf('/Date(') == 0) {
                value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
            }
    
            current = $(element).datepicker("getDate");
    
            if (value - current !== 0) {
                $(element).datepicker("setDate", value);
            }
        }
    };
    

    Changes are to the change event handler to pass the entered value and not the date to the validation scripts first, then only setting the date to the observable if it is valid. I also added the validationCore.init that is needed for custom bindings discussed here:

    http://github.com/ericmbarnard/Knockout-Validation/issues/69

    I also added rpenrose's suggestion for a blur on change to eliminate some pesky datepicker scenarios getting in the way of things.

    0 讨论(0)
  • 2020-11-22 16:28

    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.

    0 讨论(0)
  • 2020-11-22 16:33

    Although all of these answers saved me a lot of work, none of them fully worked for me. After selecting a date, the binded value would not update. I could only get it to update when changing the date value using the keyboard then clicking out of the input box. I fixed this by augmenting RP Niemeyer's code with syb's code to get:

    ko.bindingHandlers.datepicker = {
            init: function (element, valueAccessor, allBindingsAccessor) {
                //initialize datepicker with some optional options
                var options = allBindingsAccessor().datepickerOptions || {};
    
                var funcOnSelectdate = function () {
                    var observable = valueAccessor();
                    observable($(element).datepicker("getDate"));
                }
    
                options.onSelect = funcOnSelectdate;
    
                $(element).datepicker(options);
    
                //handle the field changing
                ko.utils.registerEventHandler(element, "change", funcOnSelectdate);
    
                //handle disposal (if KO removes by the template binding)
                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(element).datepicker("destroy");
                });
    
            },
            update: function (element, valueAccessor) {
    
                var value = ko.utils.unwrapObservable(valueAccessor());
                if (typeof(value) === "string") { // JSON string from server
                    value = value.split("T")[0]; // Removes time
                }
    
                var current = $(element).datepicker("getDate");
    
                if (value - current !== 0) {
                    var parsedDate = $.datepicker.parseDate('yy-mm-dd', value);
                    $(element).datepicker("setDate", parsedDate);
                }
            }
        };
    

    I suspect putting the observable($(element).datepicker("getDate")); statement in its own function and registering that with options.onSelect did the trick?

    0 讨论(0)
提交回复
热议问题