Conceptos
Normalizando
8 min
los editores de slate pueden editar estructuras de datos complejas y anidadas y en su mayor parte, esto es genial pero en ciertos casos, se pueden introducir inconsistencias en la estructura de datos, más a menudo cuando se permite a un usuario pegar contenido de texto enriquecido arbitrario "normalizar" es cómo puedes asegurarte de que el contenido de tu editor siempre tenga una cierta forma es similar a "validar", excepto que en lugar de solo determinar si el contenido es válido o inválido, su trabajo es arreglar el contenido para que vuelva a ser válido restricciones incorporadas los editores de slate vienen con algunas restricciones incorporadas desde el principio estas restricciones están ahí para hacer que trabajar con contenido mucho más predecible que el estándar contenteditable toda la lógica incorporada en slate depende de estas restricciones, así que, desafortunadamente, no puedes omitirlas ellas son todos element los nodos deben contener al menos un text descendiente si un nodo de elemento no contiene hijos, se añadirá un nodo de texto vacío como su único hijo esta restricción existe para asegurar que los puntos de anclaje y enfoque de la selección (que dependen de hacer referencia a nodos de texto) siempre puedan colocarse dentro de cualquier nodo con esto, los elementos vacíos (o elementos vacíos) no serían seleccionables dos textos adyacentes con las mismas propiedades personalizadas se fusionarán si dos nodos de texto adyacentes tienen el mismo formato, se fusionan en un solo nodo de texto con una cadena de texto combinada de los dos esto existe para evitar que los nodos de texto solo aumenten en número en el documento, ya que tanto agregar como eliminar formato resulta en la división de nodos de texto los nodos de bloque solo pueden contener otros bloques, o nodos en línea y de texto por ejemplo, un párrafo no puede tener otro párrafo elemento de bloque y un enlace elemento en línea como hijos al mismo tiempo el tipo de hijos permitidos está determinado por el primer hijo, y cualquier otro hijo no conforme se elimina esto asegura que comportamientos comunes de richtext como "dividir un bloque en dos" funcionen de manera consistente los nodos en línea no pueden ser el primer o último hijo de un bloque padre, ni pueden estar al lado de otro nodo en línea en el array de hijos si este es el caso, se añadirá un nodo de texto vacío para corregir esto y cumplir con la restricción el nodo editor de nivel superior solo puede contener nodos de bloque si alguno de los hijos de nivel superior son nodos en línea o de texto, serán eliminados esto asegura que siempre haya nodos de bloque en el editor para que comportamientos como "dividir un bloque en dos" funcionen como se espera estas restricciones predeterminadas son todas obligatorias porque hacen que trabajar con documentos de slate mucho más predecible 🤖 aunque estas restricciones son las mejores que hemos ideado hasta ahora, siempre estamos buscando formas de que las restricciones integradas de slate sean menos restrictivas si es posible, siempre que mantenga comportamientos estándar fáciles de razonar si se te ocurre una forma de reducir o eliminar una restricción integrada con un enfoque diferente, ¡estamos atentos! añadiendo restricciones las restricciones integradas son bastante genéricas pero también puedes agregar tus propias restricciones además de las integradas que son específicas para tu dominio para hacer esto, extiendes la normalizenode función en el editor la normalizenode función se llama cada vez que se aplica una operación que inserta o actualiza un nodo (o sus descendientes), dándote la oportunidad de asegurarte de que los cambios no lo dejaron en un estado inválido, y corrigiendo el nodo si es así por ejemplo, aquí hay un plugin que asegura que los bloques de párrafo solo tengan texto o elementos en línea como hijos 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 } este ejemplo es bastante simple cada vez que se llama a normalizenode en un elemento de párrafo, recorre cada uno de sus hijos asegurándose de que ninguno de ellos sea un elemento de bloque y si uno es un elemento de bloque, se desenvuelve, de modo que el bloque se elimina y sus hijos ocupan su lugar el nodo está "arreglado" pero, ¿qué pasa si el hijo tiene bloques anidados? normalización de múltiples pasadas una cosa que debes entender sobre las restricciones de normalizenode es que son de múltiples pasadas si revisas el ejemplo anterior nuevamente, notarás la declaración de retorno if (element iselement(child) && !editor isinline(child)) { transforms unwrapnodes(editor, { at childpath }) return } al principio podrías pensar que esto es extraño, porque con el return ahí, el original normalizenodes nunca será llamado, y las restricciones integradas no tendrán la oportunidad de ejecutar sus propias normalizaciones pero, hay un pequeño "truco" para normalizar cuando llamas a editor unwrapnodes , en realidad estás cambiando el contenido del nodo que se está normalizando actualmente así que, aunque estés terminando el pase de normalización actual, al hacer un cambio en el nodo estás iniciando un nuevo pase de normalización esto resulta en una especie de recursivo normalizando esta característica de múltiples pases hace que sea mucho más fácil escribir normalizaciones, porque solo tienes que preocuparte por arreglar un solo problema a la vez, y no por arreglar cada posible problema que podría estar poniendo un nodo en un estado inválido para ver cómo funciona esto en la práctica, comencemos con este documento inválido \<editor> \<paragraph a> \<paragraph b> \<paragraph c>word\</paragraph> \</paragraph> \</paragraph> \</editor> el editor comienza ejecutando normalizenode en \<paragraph c> y es válido, porque solo contiene nodos de texto como hijos pero luego, sube por el árbol y ejecuta normalizenode en \<paragraph b> este párrafo es inválido, ya que contiene un elemento de bloque ( \<paragraph c> ) así que ese bloque hijo se desenvuelve, resultando en un nuevo documento de \<editor> \<paragraph a> \<paragraph b>word\</paragraph> \</paragraph> \</editor> y al realizar esa corrección, el nivel superior \<paragraph a> cambió se normaliza, y es inválido, así que \<paragraph b> se desenvuelve, resultando en \<editor> \<paragraph a>word\</paragraph> \</editor> y ahora cuando normalizenode se ejecuta, no se realizan cambios, ¡así que el documento es válido! 🤖 en su mayor parte, no necesitas pensar en estos internos solo puedes saber que cada vez que normalizenode es llamado y detectas un estado inválido, puedes corregir ese único estado inválido y confiar en que normalizenode será llamado nuevamente hasta que el nodo se vuelva válido correcciones incorrectas sin embargo, la única trampa a evitar es crear un bucle de normalización infinito esto puede suceder si verificas una estructura inválida específica, pero luego no arreglas realmente esa estructura con el cambio que haces en el nodo esto resulta en un bucle infinito porque el nodo sigue siendo marcado como inválido, pero nunca se corrige adecuadamente por ejemplo, considera una normalización que asegurara que los elementos de enlace tengan una propiedad de url // 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 } esta corrección está escrita incorrectamente quiere asegurar que todos los elementos de enlace tengan una cadena de propiedad de url pero para corregir enlaces inválidos, establece la url en null , ¡lo cual sigue sin ser una cadena! en este caso, querrías o bien deshacer el enlace, eliminándolo por completo o ampliar tu validación para aceptar un "vacío" url == null también
