【问题标题】:jQuery UI datepicker change event not caught by KnockoutJSKnockoutJS 未捕获 jQuery UI 日期选择器更改事件
【发布时间】:2011-09-30 13:26:43
【问题描述】:

我正在尝试将 KnockoutJS 与 jQuery UI 一起使用。我有一个附加了日期选择器的输入元素。我目前正在运行knockout.debug.1.2.1.js,并且似乎更改事件从未被 Knockout 捕获。该元素如下所示:

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

我什至尝试更改 valueUpdate 事件类型,但无济于事。似乎 Chrome 会在更改值之前引发 focus 事件,但 IE 不会。

是否有一些“重新绑定所有绑定”的 Knockout 方法?从技术上讲,我只需要在将值发送回服务器之前对其进行更改。所以我可以接受这种解决方法。

我认为问题是日期选择器的错,但我不知道如何解决这个问题。

有什么想法吗?

【问题讨论】:

    标签: jquery-ui knockout.js


    【解决方案1】:

    我认为对于 jQuery UI datepicker,最好使用自定义绑定,该绑定将使用 datepicker 提供的 API 读取/写入 Date 对象。

    绑定可能看起来像(来自我的回答 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);
            }
        }
    };
    

    你会像这样使用它:

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

    这里的 jsFiddle 示例:http://jsfiddle.net/rniemeyer/NAgNV/

    【讨论】:

    • 我喜欢的是你没有在这个活页夹中偷工减料,就像 dispose 回调一样。在掌握 KnockoutJS 的道路上可以遵循的合理示例!
    • 那么绑定到动态创建的元素的日期选择器呢……我的意思是,带有实时处理程序的日期选择器。
    • Phoenix_uy:要让日期选择器处理动态创建的对象,请确保不要设置输入的 ID 或名称。
    • 我正在使用它,它工作得很好,除了一件小事 - 如果我将 minDate 或 maxDate 设置为等于一个可观察的,如果该可观察的被更改,它不会得到更新(例如,如果我有两个日期选择器,其中第一个的最大日期是第二个的值,如果我更新第二个,它不会更新第一个的最大日期)与这个问题相同stackoverflow.com/questions/14732204/…
    • 貌似事件名不对,ko.utils.registerEventHandler(element, "changeDate", function() - 应该是ko.utils.registerEventHandler(element, "change", function()
    【解决方案2】:

    这是 RP Niemeyer 的答案版本,可与此处找到的淘汰赛验证脚本一起使用: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);
            }
        }
    };
    

    更改是更改事件处理程序,以首先将输入的值而不是日期传递给验证脚本,然后仅将日期设置为可观察的(如果有效)。我还添加了此处讨论的自定义绑定所需的 validationCore.init:

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

    我还添加了 rpenrose 关于模糊更改的建议,以消除一些讨厌的日期选择器场景妨碍事情。

    【讨论】:

    • 似乎对我不起作用,我得到 TypeError: observable.isModified is not a function on line 313 of knockout.validation.js。这里的小例子:frikod.se/~capitol/fel/test.html
    • 让它与验证库一起工作的重要行是:ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor);
    【解决方案3】:

    我使用了不同的方法。由于 knockout.js 似乎不会在更改时触发事件,因此我已强制 datepicker 在关闭后调用 change() 以获取其输入。

    $(".date").datepicker({
        onClose: function() {
            $(this).change(); // Forces re-validation
        }
    });
    

    【讨论】:

    • $('.datepicker').datepicker({ onSelect: function(dateText) { $("#date_in").trigger("change"); } });
    【解决方案4】:

    虽然所有这些答案都为我节省了很多工作,但没有一个完全适合我。选择日期后,绑定的值不会更新。我只能在使用键盘更改日期值然后单击输入框时才能更新它。我通过用 syb 的代码扩充 RP Niemeyer 的代码来解决这个问题:

    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);
                }
            }
        };
    

    我怀疑把 observable($(element).datepicker("getDate"));在它自己的函数中声明并用 options.onSelect 注册它就行了?

    【讨论】:

    • 感谢一百万!我尝试了每个示例,然后在页面底部找到了这个示例,它终于可以工作了。我只是做了一个小调整,以便绑定值保持与它下降时相同的“服务器友好”格式。在你的 funcOnSelectdate 函数中使用这个: observable($.datepicker.formatDate('yy-mm-dd' , $(element).datepicker('getDate')));
    • 我认为如果你覆盖 onSelect 函数它不会引发 change 事件...
    【解决方案5】:

    感谢这篇文章,我发现它非常有用。

    如果您希望 DatePicker 的行为与 JQuery UI 默认行为完全相同,我建议在更改事件处理程序中的元素上添加模糊:

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

    【讨论】:

    • 这个答案看起来不完整?这是对@RPNiemeyer 答案的评论,还是其他人的评论?
    【解决方案6】:

    我通过更改包含的脚本文件的顺序解决了这个问题:

    <script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script>
    <script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>
    

    【讨论】:

    • 尽管输入从日期选择器中呈现了正确选择的日期,但模型未更新也存在类似问题。开始列出建议列表..但是..这绝对是我的问题。嗯.. 我的 MVC 项目在 jquery 和 jquery UI 脚本之前有很长一段时间的 KO 脚本 - 必须彻底测试。
    【解决方案7】:

    与 RP Niemeyer 相同,但更好地支持 WCF 日期时间、时区和使用 DatePicker onSelect JQuery 属性。

            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);
                }
            }
        };
    

    享受:)

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

    【讨论】:

      【解决方案8】:

      我认为可以更轻松地完成:&lt;input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" /&gt;

      因此您不需要在 init 函数中进行手动更改处理。

      但在这种情况下,您的“myDate”变量将只获得可见值,而不是 Date 对象。

      【讨论】:

        【解决方案9】:

        或者,您可以在绑定中指定:

        更新:

         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);
            }               
        }
        

        【讨论】:

        • 这解决了返回日期值是字符串格式的问题,即。 “2013-01-20T05:00:00”而不是日期对象。我在从 Web API 加载数据时遇到了这个问题。
        【解决方案10】:

        基于 Ryan 的解决方案,myDate 返回标准日期字符串,这在我的情况下不是理想的。我使用 date.js 来解析值,所以它总是会返回你想要的日期格式。看看这个例子fiddle Example

        update: function(element, valueAccessor) {
            var value = ko.utils.unwrapObservable(valueAccessor()),
                current = $(element).datepicker("getDate");
            var d = Date.parse(value);
            if (value - current !== 0) {
                $(element).datepicker("setDate", d.toString("MM/dd/yyyy"));   
            }
        }
        

        【讨论】:

          【解决方案11】:

          我需要从服务器反复更新我的数据时遇到了这个问题,但由于我需要在下面分享(我的日期格式 /Date(1224043200000)/),我并没有完全完成这项工作:

          //Object Model
          function Document(data) {
                  if (String(data.RedemptionExpiration).indexOf('/Date(') == 0) {
                      var newDate = new Date(parseInt(data.BDate.replace(/\/Date\((.*?)\)\//gi, "$1")));
                      data.RedemptionExpiration = (newDate.getMonth()+1) +
                          "/" + newDate.getDate() +
                          "/" + newDate.getFullYear();
                  }
                  this.RedemptionExpiration = ko.observable(data.RedemptionExpiration);
          }
          //View Model
          function DocumentViewModel(){
              ///additional code removed
              self.afterRenderLogic = function (elements) {
                  $("#documentsContainer .datepicker").each(function () {
                      $(this).datepicker();                   
                  });
              };
          }
          

          在为输出正确格式化模型后,我添加了一个带有文档knockoutjs的模板:

          <div id="documentsContainer">
              <div data-bind="template: { name: 'document-template', foreach: documents, afterRender: afterRenderLogic }, visible: documents().length > 0"></div>
          </div>
          
          //Inline template
          <script type="text/html" id="document-template">
            <input data-bind="value: RedemptionExpiration" class="datepicker" />
          </script>
          

          【讨论】:

            【解决方案12】:

            很少有人要求动态日期选择器选项。在我的情况下,我需要一个动态日期范围 - 所以第一个输入定义了第二个的最小值,第二个设置了第一个的最大值。我通过扩展 RP Niemeyer 的处理程序解决了这个问题。所以对他的原作:

            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, "change", 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);
                    }
                }
            };
            

            我已经添加了两个与我想要修改的选项相对应的处理程序:

            ko.bindingHandlers.minDate = {
                update: function(element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor()),
                        current = $(element).datepicker("option", "minDate", value);
                }
            };
            
            ko.bindingHandlers.maxDate = {
                update: function(element, valueAccessor) {
                    var value = ko.utils.unwrapObservable(valueAccessor()),
                        current = $(element).datepicker("option", "maxDate", value);
                }
            };
            

            并在我的模板中使用它们:

            <input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" />       
            <input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />
            

            【讨论】:

              【解决方案13】:

              并非总是可以使用先前答案中提供的自定义绑定。调用 $(element).datepicker(...) 需要相当长的时间,例如,如果您有几十个甚至数百个元素可以调用此方法,则必须“懒惰”地执行此操作,即按需执行。

              例如,视图模型可能会被初始化,inputs 被插入到 DOM 中,但相应的日期选择器只会在用户单击它们时被初始化。

              所以,这是我的解决方案:

              添加允许将任意数据附加到节点的自定义绑定:

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

              使用绑定来附加用于input 值的可观察对象:

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

              最后,在初始化日期选择器时,使用 onSelect 选项:

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

              这样,每次用户使用 datepicker 更改日期时,相应的 Knockout observable 也会更新。

              【讨论】:

                猜你喜欢
                • 2017-09-03
                • 2016-11-26
                • 1970-01-01
                • 2015-06-12
                • 1970-01-01
                • 1970-01-01
                • 2013-12-17
                • 1970-01-01
                相关资源
                最近更新 更多