淘汰赛:避免在绑定中进行循环更新 [英] Knockout: avoid circular updates in a binding
问题描述
我有一个自定义绑定,可与内容可编辑的所见即所得编辑器 Scribe 进行交互.当编辑器内容更改时,它将更新关联的可观察对象,并在关联的可观察对象进行以下操作时更新编辑器:
I have a custom binding that interfaces with Scribe, a contenteditable WYSIWYG editor. It updates the associated observable when the editor content changes, and updates the editor when the associated observable does:
ko.bindingHandlers.editor = {
init: function(element, valueAccessor) {
var $element = $(element),
content = ko.unwrap(valueAccessor()),
scribe;
// create a new editor instance
scribe = new Scribe(element);
// store it for later access in our 'update' handler
$element.data('scribe', scribe);
// set the initial editor content
scribe.setContent(content);
// update the observable whenever the editor changes
scribe.on('content-changed', function() {
var observable = valueAccessor(),
content = scribe.getHTML();
observable(content);
});
},
update: function(element, valueAccessor) {
var $element = $(element),
content = ko.unwrap(valueAccessor());
// update the editor content when the observable changes
$element.data('scribe').setContent(content);
}
};
问题是这样的:
- 用户输入编辑器
- 编辑器组件发送已更改"事件
- 事件处理程序更新可观察对象
- 绑定的更新"处理程序被触发,将更改直接写回编辑器
通常,这没什么大不了的,只是不必要的.但是在我的情况下,editor.setContent
的副作用是将光标位置重置为输入的开头-您输入的所有内容都会被颠倒.
Usually that would not be a big deal, just unnecessary. But in my case, editor.setContent
has the side effect of resetting the cursor position to the beginning of the input - everything you enter is reversed.
当-且仅当-可观察对象在应用程序中的其他位置更改时,我需要更新编辑器.我正在寻找的是一种避免使用最初来自编辑器本身的更改来更新编辑器的方法.
I need the editor to be updated, if - and only if - the observable is changed somewhere else in the application. What I am looking for is a way to avoid updating the editor with changes that originated in the editor itself in the first place.
如何最好地避免这些循环更新?
How can I best avoid these circular updates?
更新: 我发现了一种避免我眼前一个问题的明显方法-在更新处理程序中,在进行更新之前,只需将可观察的内容与当前编辑器的内容进行比较即可:
Update: I have found an obvious way of sidestepping my immediate problem - in the update handler, simply compare the observable content to the current editor content before making the update:
if ($element.data('scribe').getHTML() !== content) {
$element.data('scribe').setContent(content);
}
即使只是为了避免不必要的更新调用,我仍然会对通用答案感兴趣.
I would still be interested in a general answer, if just for the sake of avoiding unnecessary update calls.
推荐答案
Based on RPNiemeyers recommendation, I moved the observable -> component updating functionality from the bindings' update
handler into the init
handler:
ko.bindingHandlers.editor = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var $element = $(element),
content = ko.unwrap(valueAccessor()),
ignoreObservableChanges = false,
scribe;
scribe = new Scribe(element);
$element.data('scribe', scribe);
scribe.setContent(content);
scribe.on('content-changed', function() {
var observable = valueAccessor(),
content = scribe.getHTML();
ignoreObservableChanges = true;
observable(content);
ignoreObservableChanges = false;
});
valueAccessor().subscribe(function(newValue) {
if (ignoreObservableChanges) {
return;
}
scribe.setContent(newValue);
});
}
};
组件->可观察更新程序和可观察->组件更新程序现在在其作用域内共享ignoreObservableChanges
标志,因此前者可以指示后者何时跳过更新.
Both the component -> observable updater and the observable -> component updater now share the ignoreObservableChanges
flag in their scope, so the former can instruct the latter when to skip an update.
这篇关于淘汰赛:避免在绑定中进行循环更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!