Почему привязка нокаутированного шаблона перестает работать после ручного изменения порядка и возврата элементов dom? - PullRequest
0 голосов
/ 29 января 2019

Я использую привязку нокаута foreach (точнее, template: { foreach: items }) для отображения списка элементов.Затем я продолжаю выполнять следующие действия:

  1. Поменяйте местами первый и второй элементы наблюдаемого массива.Я вижу изменения, отображаемые на экране, как и ожидалось.
  2. Повторите предыдущее действие, чтобы вернуться в исходное состояние.Опять же, это работает как ожидалось.
  3. Теперь поменяйте местами первый и второй элементы DOM.Я вижу изменения, отображаемые на экране, как и ожидалось.
  4. Повторите предыдущее действие, чтобы вернуться в исходное состояние.Опять же, это работает, как и ожидалось.

Несмотря на то, что мы вручную подделали DOM, мы вернулись точно в исходное состояние, не вызывая нокаут во время подделки DOM.Это означает, что состояние восстанавливается до того момента, когда нокаут узнал о нем, поэтому он должен выглядеть так, как будто ничего не изменилось с самого начала.Однако, если я снова выполню первое действие, то есть поменяю местами первые два элемента в массиве, изменения не будут отображены на экране.

Вот jsfiddle для иллюстрации проблемы: https://jsfiddle.net/k7u5wep9/.

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

Мой вопрос: что вызывает такое поведение?А потом, как обойти это?

Ответы [ 3 ]

0 голосов
/ 29 января 2019
  • Управляя DOM, вы нарушили сделанную привязку.
  • Не манипулируйте напрямую DOM.Knockout не обнаружит сделанные изменения.
0 голосов
/ 29 января 2019

Оказывается, здесь не происходит ничего волшебного.Ошибка, которую я сделал, состояла в том, чтобы рассматривать только элементы вместо всех узлов.При переупорядочении привязка к выбивающему шаблону сохраняет записи всех узлов, а не только элементов.

Перед ручным редактированием DOM дочерние узлы привязки шаблона:

NodeList(6) [text, div, text, text, div, text].

После ручной замены первых двух элементов с помощью parent.insertBefore(parent.children[1], parent.children[0]) это превращается в:

NodeList(6) [text, div, div, text, text, text].

Повтор действия приводит к:

NodeList(6) [text, div, div, text, text, text].

Хотя это идентично исходному состоянию при обращении только к элементам , оно совершенно отличается при обращении ко всем узлам .

Решение теперь становится ясным.Один из способов выполнить правильный ручной обмен - это заменить

parent.insertBefore(parent.children[1], parent.children[0]);

на

let nexts = [parent.children[0].nextSibling, parent.children[1].nextSibling];
parent.insertBefore(parent.children[1], nexts[0]);
parent.insertBefore(parent.children[0], nexts[1]);

, как показано в https://jsfiddle.net/k7u5wep9/2/.

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

0 голосов
/ 29 января 2019

Если вы поместите with: items вокруг вашего foreach, он, по крайней мере, продолжает работать, но требует двойного щелчка, если dom order! = Порядок массива ... может привести вас в движение по крайней мере, возможно, вы можете переупорядочить ko-массив внутри функции dom для синхронизации их «приказов»?

let vm = {
  items: ko.observableArray(['item1', 'item2']),
  reorder_array() {
    vm.items([vm.items()[1], vm.items()[0]]);
  },
  reorder_dom() {
    let parent = document.querySelector('#items');
    parent.insertBefore(parent.children[1], parent.children[0]);
    vm.reorder_array();
  }
};
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="with: items">
  <div id="items" data-bind="template: { foreach: $data }">
    <div data-bind="text: $data"></div>
  </div>
</div>
<button data-bind="click: reorder_array">Reorder array</button>
<button data-bind="click: reorder_dom">Reorder DOM</button>
<div>
  Reorder the array twice, then reorder DOM twice. This should work as expected, and end up with the initial state. Then, try to reorder the array again. It should not work. Why?
</div>
...