淘汰赛:避免绑定中的循环更新

时间:2014-09-05 12:23:49

标签: knockout.js

我有一个与Scribe接口的自定义绑定,这是一个令人满意的WYSIWYG编辑器。它在编辑器内容更改时更新关联的observable,并在关联的observable执行时更新编辑器:

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

问题在于:

  1. 用户输入编辑器
  2. 编辑器组件发送“已更改”事件
  3. 事件处理程序更新observable
  4. 触发绑定的“更新”处理程序,将更改写回编辑器
  5. 通常这不是什么大问题,只是没必要。但就我而言,editor.setContent的副作用是将光标位置重置为输入的开头 - everything you enter is reversed

    我需要更新编辑器,如果 - 且仅当 - 在应用程序的其他位置更改了observable。我正在寻找的是一种避免使用源自编辑器本身的更改来更新编辑器的方法。

    我怎样才能最好地避免这些循环更新?

    更新 我发现了一种避免直接问题的明显方法 - 在更新处理程序中,只需在进行更新之前将可观察内容与当前编辑器内容进行比较:

      if ($element.data('scribe').getHTML() !== content) {
        $element.data('scribe').setContent(content);
      }
    

    如果只是为了避免不必要的更新调用,我仍然会对一般答案感兴趣。

1 个答案:

答案 0 :(得分:1)

基于RPNiemeyers recommendation,我移动了observable - >组件更新功能来自绑定' update处理程序init处理程序:

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标志,因此前者可以指示后者何时跳过更新。