Concepts
Normalisation
8 min
les éditeurs slate peuvent éditer des structures de données complexes et imbriquées et pour la plupart, c'est génial mais dans certains cas, des incohérences dans la structure des données peuvent être introduites—le plus souvent lorsque l'on permet à un utilisateur de coller du contenu riche arbitraire la "normalisation" est la façon dont vous pouvez vous assurer que le contenu de votre éditeur a toujours une certaine forme c'est similaire à la "validation", sauf qu'au lieu de simplement déterminer si le contenu est valide ou invalide, son rôle est de corriger le contenu pour le rendre à nouveau valide contraintes intégrées les éditeurs slate sont livrés avec quelques contraintes intégrées dès le départ ces contraintes sont là pour rendre le travail avec le contenu beaucoup plus prévisible que le standard contenteditable toute la logique intégrée dans slate dépend de ces contraintes, donc malheureusement vous ne pouvez pas les omettre elles sont tous les nœuds élément doivent contenir au moins un descendant texte si un nœud élément ne contient aucun enfant, un nœud texte vide sera ajouté comme son seul enfant cette contrainte existe pour garantir que les points d'ancrage et de focus de la sélection (qui dépendent de la référence aux nœuds texte) peuvent toujours être placés à l'intérieur de n'importe quel nœud avec cela, les éléments vides (ou éléments vides) ne seraient pas sélectionnables deux textes adjacents avec les mêmes propriétés personnalisées seront fusionnés si deux nœuds texte adjacents ont le même formatage, ils sont fusionnés en un seul nœud texte avec une chaîne de texte combinée des deux cela existe pour empêcher les nœuds texte de ne jamais faire qu'augmenter en nombre dans le document, puisque l'ajout et la suppression de formatage entraînent la division des nœuds texte les nœuds de bloc ne peuvent contenir que d'autres blocs, ou des nœuds en ligne et des nœuds de texte par exemple, un paragraphe ne peut pas avoir un autre paragraphe élément de bloc et un lien élément en ligne comme enfants en même temps le type d'enfants autorisés est déterminé par le premier enfant, et tout autre enfant non conforme est supprimé cela garantit que des comportements riches de texte courants comme "diviser un bloc en deux" fonctionnent de manière cohérente les nœuds en ligne ne peuvent pas être le premier ou le dernier enfant d'un bloc parent, ni être à côté d'un autre nœud en ligne dans le tableau des enfants si tel est le cas, un nœud de texte vide sera ajouté pour corriger cela afin de respecter la contrainte le nœud éditeur de niveau supérieur ne peut contenir que des nœuds de bloc si l'un des enfants de niveau supérieur est un nœud en ligne ou un nœud de texte, il sera supprimé cela garantit qu'il y a toujours des nœuds de bloc dans l'éditeur afin que des comportements comme "diviser un bloc en deux" fonctionnent comme prévu ces contraintes par défaut sont toutes imposées car elles rendent le travail avec les documents slate beaucoup plus prévisible 🤖 bien que ces contraintes soient les meilleures que nous ayons trouvées jusqu'à présent, nous cherchons toujours des moyens de rendre les contraintes intégrées de slate moins contraignantes si possible—tant que cela permet de garder les comportements standards faciles à comprendre si vous trouvez un moyen de réduire ou de supprimer une contrainte intégrée avec une approche différente, nous sommes tout ouïe ! ajout de contraintes les contraintes intégrées sont assez génériques mais vous pouvez également ajouter vos propres contraintes en plus de celles intégrées qui sont spécifiques à votre domaine pour ce faire, vous étendez la normalizenode fonction sur l'éditeur la normalizenode fonction est appelée chaque fois qu'une opération est appliquée qui insère ou met à jour un nœud (ou ses descendants), vous donnant l'opportunité de vous assurer que les modifications ne l'ont pas laissé dans un état invalide, et de corriger le nœud si c'est le cas par exemple, voici un plugin qui garantit que les paragraphes n'ont que du texte ou des éléments en ligne comme enfants import { transforms, element, node } from 'slate' const withparagraphs = editor => { const { normalizenode } = editor editor normalizenode = entry => { const \[node, path] = entry // if the element is a paragraph, ensure its children are valid if (element iselement(node) && node type === 'paragraph') { for (const \[child, childpath] of node children(editor, path)) { if (element iselement(child) && !editor isinline(child)) { transforms unwrapnodes(editor, { at childpath }) return } } } // fall back to the original `normalizenode` to enforce other constraints normalizenode(entry) } return editor } cet exemple est assez simple chaque fois que normalizenode est appelé sur un élément de paragraphe, il parcourt chacun de ses enfants en s'assurant qu'aucun d'eux n'est un élément de bloc et si l'un d'eux est un élément de bloc, il est déballé, de sorte que le bloc est supprimé et que ses enfants prennent sa place le nœud est "réparé" mais que se passe t il si l'enfant a des blocs imbriqués ? normalisation multi pass une chose à comprendre sur les contraintes de normalizenode est qu'elles sont multi pass si vous vérifiez à nouveau l'exemple ci dessus, vous remarquerez la déclaration if (element iselement(child) && !editor isinline(child)) { transforms unwrapnodes(editor, { at childpath }) return } vous pourriez d'abord penser que c'est étrange, car avec le return là, le normalizenodes original ne sera jamais appelé, et les contraintes intégrées n'auront pas la chance d'exécuter leurs propres normalisations mais, il y a un léger "truc" pour normaliser lorsque vous appelez editor unwrapnodes , vous changez en fait le contenu du nœud qui est actuellement en cours de normalisation donc même si vous terminez le passage de normalisation actuel, en apportant un changement au nœud, vous déclenchez un nouveau passage de normalisation cela entraîne une sorte de normalisation récursive cette caractéristique de passage multiple rend les normalisations beaucoup plus faciles à écrire, car vous n'avez à vous soucier que de corriger un seul problème à la fois, et non de corriger chaque problème possible qui pourrait mettre un nœud dans un état invalide pour voir comment cela fonctionne en pratique, commençons par ce document invalide \<editor> \<paragraph a> \<paragraph b> \<paragraph c>word\</paragraph> \</paragraph> \</paragraph> \</editor> l'éditeur commence par exécuter normalizenode sur \<paragraph c> et c'est valide, car il ne contient que des nœuds de texte comme enfants mais ensuite, il remonte l'arbre et exécute normalizenode sur \<paragraph b> ce paragraphe est invalide, car il contient un élément de bloc ( \<paragraph c> ) donc ce bloc enfant est déballé, ce qui donne un nouveau document de \<editor> \<paragraph a> \<paragraph b>word\</paragraph> \</paragraph> \</editor> et en effectuant cette correction, le niveau supérieur \<paragraph a> a changé il est normalisé, et il est invalide, donc \<paragraph b> est déballé, ce qui donne \<editor> \<paragraph a>word\</paragraph> \</editor> et maintenant, quand normalizenode s'exécute, aucune modification n'est apportée, donc le document est valide ! 🤖 pour la plupart, vous n'avez pas besoin de penser à ces détails internes vous pouvez simplement savoir que chaque fois que normalizenode est appelé et que vous repérez un état invalide, vous pouvez corriger cet état invalide unique et faire confiance à ce que normalizenode sera appelé à nouveau jusqu'à ce que le nœud devienne valide corrections incorrectes le piège à éviter cependant est de créer une boucle de normalisation infinie cela peut se produire si vous vérifiez une structure invalide spécifique, mais ensuite ne corrigez pas réellement cette structure avec le changement que vous apportez au nœud cela entraîne une boucle infinie car le nœud continue d'être signalé comme invalide, mais il n'est jamais corrigé correctement par exemple, considérez une normalisation qui garantissait que les éléments de lien ont une propriété url valide // warning this is an example of incorrect behavior! const withlinks = editor => { const { normalizenode } = editor editor normalizenode = entry => { const \[node, path] = entry if ( element iselement(node) && node type === 'link' && typeof node url !== 'string' ) { // error null is not a valid value for a url transforms setnodes(editor, { url null }, { at path }) return } normalizenode(entry) } return editor } cette correction est mal écrite elle veut s'assurer que tous les éléments de lien ont une chaîne de propriété url valide mais pour corriger les liens invalides, elle définit le url sur null , ce qui n'est toujours pas une chaîne ! dans ce cas, vous voudriez soit déballer le lien, en le supprimant complètement ou élargir votre validation pour accepter un "vide" url == null également
