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:
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?
3 Réponses :
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);
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.
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.
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 >