Расширение наблюдаемого в пользовательском связывании
У меня есть собственный обработчик привязки, который я привязываю к сложному объекту в моей модели представления.
Обработчик привязки работает правильно и update
Функция вызывается, когда обновляется любое из свойств наблюдаемой. Тем не менее update
Функция вызывается для каждого обновленного свойства, что приводит к странному поведению, так как я полагаюсь на весь объект, который будет доступен и обновлен.
Я понимаю, почему это происходит, поскольку каждое свойство вызывает вызов обновления, и я думаю, что знаю, как это предотвратить - используя функциональность отложенных обновлений Knockout.
Тем не менее, я не могу найти, как включить отложенные обновления только для наблюдаемого в моей пользовательской привязке. Я не хочу включать его в приложение, так как пишу привязку как библиотечную функцию.
Я пробовал много разных методов, в том числе:
- пытается расширить сам обработчик привязки;
- расширение
init
функция; - расширение
valueAccessor
; - заменяя
valueAccessor
с новым наблюдаемым сdeferred
применяется; - создание вычисляемой наблюдаемой и повторная привязка элемента;
Все из которых не сработали.
Я не нашел другого обработчика привязки, который бы отдаленно приближался к такого рода функциям и пытался соединить его вместе с другими функциями.
Мой код привязки сам по себе относительно прост: я беру связанный объект и просто разделяю параметры и передаю их экземпляру Code Mirror.
ko.bindingHandlers.editor = {
init: function(element, valueAccessor, allBindingsAccessor) {
var observableValue = ko.utils.unwrap(valueAccessor());
initEditor(element, observableValue, allBindingsAccessor);
},
update: function(element, valueAccessor, allBindingsAccessor) {
var observableValue = ko.unwrap(valueAccessor());
createEditor(codeEditorDiv, observableValue);
resize();
updateEditor(element, observableValue, allBindingsAccessor);
}
};
И мой HTML-код:
<div id="editor" data-bind="editor: EditorVM"></div>
Я использую Dotnetify для ViewModel, так что это разумный сложный класс C#, но достаточно сказать, что привязка работает и обновляется, но мне нужно, чтобы он вызывал только "update", как только все свойства были обновлены.
1 ответ
К сожалению, вы не показали, что initEditor
, createEditor
а также updateEditor
делать с observableValue
потому что это, вероятно, где вы должны расширять свои наблюдаемые.
init
а также update
методы привязки создают вычисляемые зависимости, это означает, что любая наблюдаемая, развернутая в стеке вызовов, начиная с init
вызовет update
метод, который будет вызван.
В абстрактном примере:
const someVM = ko.observable({
a: ko.observable(1),
b: ko.observable(2),
c: ko.observable(3)
});
// Some function that unwraps properties
const logABC = function(vm) {
console.log(
vm.a(),
vm.b(),
vm.c()
);
}
// Regular binding update:
ko.computed(function update() {
console.log(
"Regular binding update:",
)
logABC(someVM())
});
// Change VM
someVM(someVM());
// Change a, b, and c
someVM().a("A");
someVM().b("B");
someVM().c("C");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Обратите внимание, что update
называется:
- При инициализации вычисляется
- При изменении наблюдаемой, содержащей модель представления
- При изменении любого из наблюдаемых свойств модели представления
Есть несколько способов решения проблемы, из которых самым простым является создание собственного computed
внутри init
метод вашей привязки и расширить его до deferred
,
const someVM = ko.observable({
a: ko.observable(1),
b: ko.observable(2),
c: ko.observable(3)
});
const getABC = function(vm) {
return [vm.a(), vm.b(), vm.c()].join(", ");
}
ko.bindingHandlers.renderABC = {
init: function(el, va) {
el.innerText += "Init.\n";
// This ensures any inner unwrapping gets deferred
var updateSub = ko.computed(function update() {
el.innerText += getABC(ko.unwrap(va())) + "\n";
}).extend({ deferred: true });
ko.utils.domNodeDisposal.addDisposeCallback(el, function() {
updateSub.dispose();
});
}
}
ko.applyBindings({ someVM: someVM });
// Change VM
someVM(someVM());
// Change a, b, and c
someVM().a("A");
someVM().b("B");
someVM().c("C");
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<pre data-bind="renderABC: someVM"></pre>