Расширение наблюдаемого в пользовательском связывании

У меня есть собственный обработчик привязки, который я привязываю к сложному объекту в моей модели представления.

Обработчик привязки работает правильно и update Функция вызывается, когда обновляется любое из свойств наблюдаемой. Тем не менее update Функция вызывается для каждого обновленного свойства, что приводит к странному поведению, так как я полагаюсь на весь объект, который будет доступен и обновлен.

Я понимаю, почему это происходит, поскольку каждое свойство вызывает вызов обновления, и я думаю, что знаю, как это предотвратить - используя функциональность отложенных обновлений Knockout.

Тем не менее, я не могу найти, как включить отложенные обновления только для наблюдаемого в моей пользовательской привязке. Я не хочу включать его в приложение, так как пишу привязку как библиотечную функцию.

Я пробовал много разных методов, в том числе:

Все из которых не сработали.

Я не нашел другого обработчика привязки, который бы отдаленно приближался к такого рода функциям и пытался соединить его вместе с другими функциями.

Мой код привязки сам по себе относительно прост: я беру связанный объект и просто разделяю параметры и передаю их экземпляру 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 называется:

  1. При инициализации вычисляется
  2. При изменении наблюдаемой, содержащей модель представления
  3. При изменении любого из наблюдаемых свойств модели представления

Есть несколько способов решения проблемы, из которых самым простым является создание собственного 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>

Другие вопросы по тегам