Durandal JS 2.0 で構築されたアプリケーションがあり、John Papa による CodeCamper SPA からインスピレーションを得たパーツを使用しています。フォームの 1 つで、日付と時刻のピッカーを使用したいので、見栄えがよく、私のニーズを満たすhttp://www.malot.fr/bootstrap-datetimepickerを見つけました。これは、Knockout JS とカスタム バインディング ハンドラーを使用してデータ レイヤーに接続され、そのように初期化されます。
コントロールは視覚的にもデータも問題なく機能し、見栄えも良いことに注意してください。データモデルと基礎となるデータベースを問題なく更新します。このdatetimepickerを除いて、他のすべての標準コントロールは正常に機能します。
私の問題は、durandal フォームが構成され、datetimepicker が DOM double に挿入されたときに発生します。フォームを開くたびに、ピッカーが再び挿入されます。DOM に膨大な量の datetimepicker が挿入されているため、しばらくするとアプリケーション全体の速度が低下し始めます。
これがどこで発生するかを追跡しようとしましたが、オブザーバブルを初期化または再初期化するたびに発生するようです。これは、フォームがアクティブ化されたとき、およびデータが取得されたときに発生します。これにより 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];
}