jQuery UI datepicker更改事件未被KnockoutJS捕获


134

我正在尝试将KnockoutJS与jQuery UI结合使用。我有一个附有日期选择器的输入元素。我目前正在跑步knockout.debug.1.2.1.js,而且变更事件似乎从未被淘汰赛捕获。该元素如下所示:

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

我什至尝试更改valueUpdate事件类型,但无济于事。Chrome似乎focus在更改值之前就引发了一个事件,但是IE却没有。

是否存在一些“重新绑定所有绑定”的淘汰方法?从技术上讲,我只需要更改值,然后再将其发送回服务器即可。所以我可以接受这种解决方法。

我认为问题出在日期选择器的问题上,但我不知道该如何解决。

有任何想法吗?

Answers:


253

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

绑定可能看起来像(从我的回答这里):

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/


21
我喜欢的是您没有像在dispose回调中那样在活页夹中偷工减料。在掌握KnockoutJS的道路上可以遵循的一个很好的例子!
Dav 2012年

2
而将日期选择器绑定到动态创建的元素上呢...我的意思是,带有实时处理程序的日期选择器。
Phoenix_uy 2012年

6
Phoenix_uy:为使日期选择器与动态创建的对象一起使用,请确保不要设置输入的ID或名称。
James Reategui 2013年

1
我正在使用它,并且除了一件小事之外,它都工作正常-如果将minDate或maxDate设置为可观察值,则如果该可观察值发生更改,则不会更新(例如,如果我有两个日期选择器,其中最大日期为首先是第二的价值,如果我更新了第二它不更新第一的最大日)与此相同的问题stackoverflow.com/questions/14732204/...
PW喀

2
事件名称似乎错误,ko.utils.registerEventHandler(element,“ changeDate”,function()-应该为ko.utils.registerEventHandler(element,“ change”,function()
Adam Bilinski

13

这是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的建议,建议对更改进行模糊处理,以消除某些讨厌的datepicker场景妨碍操作的方式。


2
似乎不适合我,我得到TypeError:observable.isModified不是基因敲除.validation.js的第313行上的函数。这里的小例子:frikod.se/~capitol/fel/test.html
AlexanderKjäll

使它与验证库一起使用的重要行是:ko.bindingHandlers.validationCore.init(element,valueAccessor,allBindingsAccessor);
CRice

11

我使用了不同的方法。由于kickout.js似乎并未在更改时触发事件,因此我已强制datepicker一旦关闭,就对其输入调用change()。

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

1
$('。datepicker')。datepicker({onSelect:function(dateText){$(“#date_in”)。trigger(“ change”);}}));
elsadek 2014年

9

尽管所有这些答案都为我节省了很多工作,但没有一个对我有帮助。选择日期后,绑定值将不会更新。我只能在使用键盘更改日期值然后在输入框之外单击时更新它。我通过用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注册该技巧?


2
太感谢了!我尝试了每个示例,然后在页面底部找到了这个示例,终于可以使用了。我只是做了一个小小的调整,使绑定值保持与出现的相同的“服务器友好”格式。在您的funcOnSelectdate函数中,请使用以下命令:observable($。datepicker.formatDate('yy-mm-dd' ,$(element).datepicker('getDate')));
BrutalDev

我认为,如果您重写该onSelect功能,则不会引发该change事件……
NickL 2014年

6

感谢本文,我发现它非常有用。

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

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

        $(element).blur();

    });

这个答案看起来不完整?这是对@RPNiemeyer的答案的评论,还是对其他人的评论?
rjmunro 2012年

3

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

<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脚本很长时间了-必须进行全面测试。
bkwdesign 2014年

2

与RP Niemeyer相同,但是更好地支持WCF DateTime,Timezones和使用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/


1

我认为可以轻松得多: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

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

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


1

另外,您可以在绑定中指定:

更新:

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

2
当返回的日期值是字符串格式(即)时,这解决了一个问题。“ 2013-01-20T05:00:00”而不是日期对象。从Web API加载数据时遇到了这个问题。
James Reategui 2013年

0

基于Ryan的解决方案,myDate返回标准日期字符串,对于我而言,这不是理想的日期字符串。我使用date.js解析值,因此它将始终返回您想要的日期格式。看看这个例子小提琴例

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

0

我需要从服务器上反复更新我的数据,但是在下面我的共享需求还没有完全完成(我的日期格式/ 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();                   
        });
    };
}

在为输出正确格式化了模型之后,我添加了一个模板,其中包含文档kickoutjs

<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>

0

很少有人要求动态日期选择器选项。就我而言,我需要一个动态的日期范围-因此第一个输入定义第二个的最小值,第二个输入设置第一个的最大值。我通过扩展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" />

0

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

例如,可以初始化视图模型,将inputs插入DOM中,但是仅当用户单击它们时,相应的日期选择器才会被初始化。

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

添加一个定制绑定,该绑定允许将任意数据附加到节点:

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

使用绑定将attcah的可观察值用于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
});

这样,每当用户使用日期选择器更改日期时,相应的可观察到的淘汰赛也将更新。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.