我有一个基于 Durandal JS 2.0 构建的应用程序,并使用了 John Papa 的 CodeCamper SPA 中的灵感部分。在其中一种形式中,我想要一个日期和时间选择器,所以我发现http://www.malot.fr/bootstrap-datetimepicker看起来不错并且满足我的需求。它使用带有自定义绑定处理程序的 Knockout JS 连接到数据层,并且也这样初始化。
请注意,控件在视觉上和数据工作得很好,而且看起来也不错。它更新数据模型和底层数据库就好了。除此 datetimepicker 外,所有其他标准控件都可以正常工作。
我的问题发生在组成 durandal 表单时,将 datetimepicker 插入到 DOM double 中。每次我打开表单时,选择器都会再次插入,加倍。过了一会儿,整个应用程序开始变慢,因为我在 DOM 中插入了大量的日期时间选择器。
我试图追踪发生这种情况的位置,并且每次初始化或重新初始化可观察对象时似乎都会发生这种情况。这发生在激活表单和检索数据时。这会触发 bindinghandler 运行并插入选择器的另一个实例。
我已经尝试了不同的方法来防止选择器被创建,但似乎没有任何效果。我试图对选取器进行 HTML 初始化,但是绑定处理程序在找到控件时遇到了问题。
因此,我一直认为问题在于我初始化底层可观察到的淘汰赛的方式,或者无法使用淘汰赛绑定处理程序初始化选择器。
对此的任何想法都将受到欢迎。
这是我的应用程序中的一些选定代码:
<div class="row-fluid ui-row">
<div class="span2 ui-form-label">
Start:
</div>
<div class="span3">
<form class="form-inline">
<div id="actystartdate" class="controls input-append date">
<input class="ui-input ui-edit input-small" type="text" data-bind='datetimepicker: startTime, datetimepickerOptions: { language: "sv", pickerPosition: "bottom-left", format: "yyyy-mm-dd", weekStart: 1, todayBtn: 1, autoclose: 1, todayHighlight: 1, startView: 2, minView: 2 }' disabled readonly><span class="add-on ui-input ui-edit ui-nonedit"><i class="icon-calendar"></i></span>
</div>
<div id="actystarttime" class="controls input-append date">
<input class="ui-input ui-edit input-mini" type="text" data-bind='datetimepicker: startTime, datetimepickerOptions: { language: "sv", pickerPosition: "bottom-left", format: "hh:ii", autoclose: 1, startView: 1, minView: 0, maxView: 1, minuteStep: 15 }' disabled readonly><span class="add-on ui-input ui-edit ui-nonedit"><i class="icon-time"></i></span>
</div>
</form>
</div>
</div>
淘汰赛绑定处理程序:
ko.bindingHandlers.datetimepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datetimepickerOptions || {};
$('#' + element.parentNode.id).datetimepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "change", function () {
var value = valueAccessor();
if (ko.isObservable(value)) {
// Separate and merge date and time portions
if (Date.parse(element.value)) {
// We have an incoming date, merge with stored time and update
var incDate = new Date(element.value);
var dateTimeString = new Date(
incDate.getFullYear() + '-' + (incDate.getMonth() + 1 ) + '-' + incDate.getDate() + ' ' +
(value().getHours() < 10 ? "0" + value().getHours() : value().getHours()).toString() + ':' +
(value().getMinutes() < 10 ? "0" + value().getMinutes() : value().getMinutes()).toString() + ':00'
);
value(dateTimeString);
} else {
// We have an incoming time, merge with stored date and update
var incTime = new Date('1970-01-01 '+element.value);
var timeDateString = new Date(
value().getFullYear() + '-' + (value().getMonth() + 1 )+ '-' + value().getDate() + ' ' +
(incTime.getHours() < 10 ? "0" + incTime.getHours() : incTime.getHours()).toString() + ':' +
(incTime.getMinutes() < 10 ? "0" + incTime.getMinutes() : incTime.getMinutes()).toString() + ':00'
);
value(timeDateString);
}
}
});
},
update: function (element, valueAccessor) {
var widget = $('#' + element.parentNode.id).data("datetimepicker");
if (widget) {
widget.update(ko.utils.unwrapObservable(valueAccessor()));
widget.setValue();
}
}
};
部分取自映射从 Amplify JS 检索并映射到数据模型的数据的数据层:
mapToContext = function (dtoList, items, results, mapper, filter, sortFunction) {
var id, existingItem;
id = mapper.getDtoId(dtoList);
existingItem = items[id];
items[id] = mapper.fromDto(dtoList, existingItem);
results(items[id]); <---------- Here the bindingHandler is again triggered
return items[id];
}