未被 KnockoutJS 捕获的 jQueryUI 数据采集器更改事件

我试图在 jQuery UI 中使用 KnockoutJS。我有一个带有数据采集器的输入元素。我目前正在运行 knockout.debug.1.2.1.js,似乎更改事件从未被 Knokout 捕获。元素看起来像这样:

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

我甚至试过更改 valueUpdate事件类型,但没有用。似乎 Chrome 在改变值之前引发了 focus事件,但 IE 没有。

有没有一些 Knokout 方法可以“重新绑定所有的绑定”?在技术上,我只需要在将其发送回服务器之前更改该值。所以我可以接受这种变通方法。

我觉得是数据采集器的问题,但我不知道怎么解决。

有什么想法吗?

70967 次浏览

我认为,对于 jQuery UI 数据采集器,最好使用自定义绑定,使用数据采集器提供的 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/

我认为这可以做得更容易: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

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

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

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

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

也就是说。

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


$(element).blur();


});

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

更新:

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

下面是 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 的建议,即模糊变化,以消除一些讨厌的日期采集器场景,让事情的方式。

我用了另一种方法。由于 linkout.js 似乎不会在更改时触发事件,所以我强制 datepicker 在关闭输入时调用 change ()。

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

与 RP Niemeyer 相同,但是更好地支持 WCF DateTime、 Timezone 和 Use the 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/

虽然所有这些答案节省了我很多工作,但是没有一个对我完全有效。选择日期后,绑定的值将不会更新。我只能让它更新时,更改日期值使用键盘,然后点击输入框。我通过把 RP Niemeyer 的代码和 Syb 的代码增强来解决这个问题:

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

我怀疑将可观察的($(element) . datepicker (“ getDate”)) ; 语句放在它自己的函数中并将其注册到 options.onSelect 中是否奏效?

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

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

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

我需要重复更新我的数据从服务器偶然发现这一点,但没有完全完成我的需求分享如下(我的日期格式/日期(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();
});
};
}

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

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

很少有人要求使用动态数据采集器选项。在我的例子中,我需要一个动态的日期范围-所以第一个输入定义了第二个的最小值,第二个设置了第一个的最大值。我通过扩展 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" />

使用前面答案中提供的自定义绑定并不总是可能的。调用 $(element).datepicker(...)需要相当长的时间,如果您有几十个甚至几百个元素可以调用这个方法,那么您必须“延迟”调用,即按需调用。

例如,可以初始化视图模型,将 input插入到 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
});

这样,每当用户使用数据采集器更改日期时,相应的 Knokout 观察值也会更新。