3
votes

Pourquoi la liaison de modèle knockout cesse-t-elle de fonctionner après la réorganisation manuelle - et la restauration - des éléments dom?

J'utilise une liaison knockout foreach (plus précisément, template: {foreach: items} ) pour afficher une liste d'éléments. Je procède ensuite aux actions suivantes:

  1. Permutez les premier et deuxième éléments du tableau observable. Je vois les changements reflétés à l'écran, comme prévu.
  2. Répétez l'action précédente pour revenir à l'état initial. Encore une fois, cela fonctionne comme prévu.
  3. Maintenant, échangez les premier et deuxième éléments DOM. Je vois les changements reflétés à l'écran, comme prévu.
  4. Répétez l'action précédente pour revenir à l'état initial. Encore une fois, cela fonctionne comme prévu.

Même si nous avons falsifié manuellement le DOM, nous sommes revenus exactement à l'état initial, sans invoquer le knock-out pendant la falsification du DOM. Cela signifie que l'état est rétabli à la dernière fois que KO en a eu connaissance, il devrait donc avoir l'air de KO comme si rien n'avait jamais changé pour commencer. Cependant, si j'effectue à nouveau la première action, c'est-à-dire permute les deux premiers éléments du tableau, les modifications ne sont pas reflétées à l'écran.

Voici un jsfiddle pour illustrer le problème: https://jsfiddle.net/k7u5wep9/ .

Je sais que falsifier manuellement le DOM géré par knockout est une mauvaise idée et que cela peut conduire à un comportement indéfini. Ceci est malheureusement inévitable dans ma situation en raison du code tiers. Ce qui me dérange, c'est que, même après avoir rétabli les modifications manuelles à l'état initial exact , le knockout ne fonctionne toujours pas comme prévu.

Ma question est: qu'est-ce qui cause ce comportement? Et puis, comment contourner ce problème?


0 commentaires

3 Réponses :


1
votes

Si vous placez un with: items autour de votre foreach , il continue au moins à fonctionner mais nécessite un double-clic si dom order! = array order .. pourrait vous aider suivre au moins, peut-être pouvez-vous réorganiser le tableau ko dans la fonction dom pour garder leurs 'commandes' synchronisées?

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


2 commentaires

Jetez un œil ici: jsfiddle.net/k7u5wep9/1 . La liaison with actualise tout son corps lorsque sa valeur liée change, provoquant ainsi la réinitiation de la liaison template , au lieu d'une mise à jour différentielle (utilisez un violon avec et sans le < code> avec la liaison pour voir la différence dans la sortie de la console). Bien que cela fonctionne pour cet exemple, cela entraînera des problèmes de performances pour les listes plus volumineuses, et cela ne résout pas vraiment le problème de front.


Je suis pleinement conscient des problèmes de performances sur les listes plus volumineuses et de la solution de contournement hackish, mais comme vous l'avez dit vous-même, vous (ou la bibliothèque) enfreignez les règles de base de l'utilisation du framework knockout, vous devriez être heureux que cela fonctionne toujours. , honnêtement.



1
votes
  • En manipulant le DOM, vous avez rompu la liaison établie.
  • Ne manipulez pas directement le DOM. Knockout ne détectera pas les modifications apportées.

1 commentaires

Merci pour votre réponse. Malheureusement, cela n'est pas utile. Comme mentionné dans la question, je suis conscient que la modification directe du DOM "casse" knock-out. Ma question est de savoir pourquoi le retour du DOM ne le «corrige» pas à nouveau. J'ai également dit que c'était hors de mon contrôle - je ne peux essayer de rétablir l'état DOM qu'après que des modifications ont été apportées. On s'attendrait à ce que cela fonctionne, ce qui ne fonctionne pas, et j'aimerais savoir exactement pourquoi.



3
votes

Il s'avère qu'il ne se passe rien de magique ici. L'erreur que j'ai faite a été de ne considérer que les éléments au lieu de tous les nœuds. La liaison de modèle knockout garde un enregistrement de tous les nœuds lors de la réorganisation, pas seulement des éléments.

Avant de modifier manuellement le DOM, les nœuds enfants de la liaison de modèle sont:

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

Après avoir échangé manuellement les deux premiers éléments en utilisant parent.insertBefore (parent.children [1], parent.children [0]) , cela se transforme en:

NodeList (6) Â [text, div, div, text, text, text] code>.

La répétition de l'action donne:

NodeList (6) Â [text, div, div, text, text, text] . p>

Bien que cela soit identique à l'état initial lorsque l'on se réfère uniquement aux éléments , il en va tout autrement lorsque l'on se réfère à tous les nœuds .

La solution devient maintenant claire. Une façon d'effectuer un échange manuel correct est de remplacer

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

avec

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

comme vu dans https: // jsfiddle .net / k7u5wep9 / 2 / .

Il faut évidemment faire plus attention lorsqu'il n'y a pas de nœuds de texte avant / après, mais l'idée reste la même.

p >


0 commentaires