Issue #3222💬 RespondidoAbierto el 7 de enero de 2021por bgrand-chReacciones 0

¿Cómo actualizar/volver a renderizar después de añadir un nuevo nodo?

Respuesta rápidapor bgrand-ch

Archivo optimizado 'TextEditor.vue': '''js // ... onFontValidate () { const { rte } = this.getRteData() const spanNode = document.createElement('span') sea anchorNode = {} this.selection.childNodes.forEach(childNode => { console.log({ childNode }) if (childNode.textContent !== this.selection.anchorText) { Regreso } An...

Lee la respuesta completa abajo ↓

Pregunta

Añado manualmente un nuevo nodo a un componente renderizado.

El resultado visual está bien, pero es imposible de guardar, porque mi estilo no es con el ID GrapesJS generado automáticamente.

Para resolver manualmente este poblem, hago doble clic en el componente para entrar y luego hago clic en el exterior en el cuerpo del lienzo. Luego, se añaden el ID GrapesJS y 'data-gjs-type='text'' al nuevo nodo, y es posible guardar.

Vue.js:

Una parte del archivo 'Main.vue': '''js Editor de texto https://grapesjs.com/docs/api/rich_text_editor.html https://css-tricks.com/creating-vue-js-component-instances-programmatically/ const editorToolbar = editorRte.getToolbarEl() const TextEditorClass = Vue.extend(TextEditor) const textEditor = nuevo TextEditorClass({ propsData: { editor: this.editor } })

textEditor.$mount()

editorToolbar.innerHTML = '' editorToolbar.appendChild(textEditor.$el) editorToolbar.classList.replace('gjs-one-bg', 'bg-primary')


Una parte del archivo 'TextEditor.vue':
'''js
Métodos: {
    Vale, no hay problema
    onButtonClick (nombre) {
      const { rte } = this.getRteData()

rte.exec(nombre)

console.log('onButtonClick()', { nombre, rte })
    },

Problema, no hay refresco/re-renderizado después de añadir spanNode
    onFontValidate () {
      const { rte } = this.getRteData()

sea anchorNode = {}

this.selection.childNodes.forEach(childNode => {
        if (childNode.textContent !== this.selection.anchorText) {
          Regreso
        }

AnchorNode = childNode
      })

const spanNode = document.createElement('span')
      rango const = nuevo rango()

spanNode.setAttribute('data-gjs-type', 'text')
      spanNode.style.fontFamily = 'Mensajero Nuevo'

rango.setStart(anchorNode, esto.selección.rangoInicio)
      rango.finSet(NoAncla, esto.selección.finRango)
      rango.surroundContents(spanNode)

rte.selection().removeAllRanges()
      rte.selection().addRange(rango)

this.editor.runCommand('core:component-exit')

console.log('onFontValidate()', {
        rte,
        rango,
        Seleccionado: this.editor.getSelected()
      })
    },

onMenuShow () {
      const {
        rte,
        selección,
        AnchorNode
      } = this.getRteData()

https://developer.mozilla.org/en-US/docs/Web/API/Selection
      https://javascript.info/selection-range#selecting-parts-of-text-nodes
      this.selection.childNodes = rte.el.childNodes
      this.selection.anchorText = anchorNode.textContent
      this.selection.rangeStart = selection.anchorOffset
      this.selection.rangeEnd = selection.focusOffset

console.log('onMenuShow()', {
        childNodes: this.selection.childNodes,
        anchorText: this.selection.anchorText,
        rangoInicio: esto.selección.rangoInicio,
        finfin.selección.fin.rango.fin
      })
    },

getRteData () {
      const seleccionado = this.editor.getSelected()
      const rte = selected.view.activeRte
      selección de const = rte.selection()
      const anchorNode = selection.anchorNode

return {
        rte,
        selección,
        AnchorNode
      }
    }
}

Respuestas (3)

bgrand-ch8 de enero de 2021

Archivo optimizado 'TextEditor.vue':

'''js // ... onFontValidate () { const { rte } = this.getRteData() const spanNode = document.createElement('span')

sea anchorNode = {}

this.selection.childNodes.forEach(childNode => { console.log({ childNode })

if (childNode.textContent !== this.selection.anchorText) { Regreso }

AnchorNode = childNode })

rte.selection().setBaseAndExtent( AnchorNode, this.selection.rangeStart, AnchorNode, this.selection.rangeEnd )

spanNode.style.fontFamily = 'Mensajero Nuevo' spanNode.estilo.color = 'rojo' spanNode.innerText = rte.selection().getRangeAt(0).toString()

rte.insertHTML(spanNode.outerHTML)

console.log('onFontValidate()', { rte, AnchorNode, spanNode })

rango const = nuevo rango() rango.setStart(anchorNode, esto.selección.rangoInicio) rango.finSet(NoAncla, esto.selección.finRango) rango.surroundContents(spanNode) rte.selection().removeAllRanges() rte.selection().addRange(rango) } // ....


Pero el problema persiste:
- ✔️ El nodo 'span' reemplaza el contenido anterior de la selección por estilos 'font-family' y 'color'.
- ❌ el nodo 'span' no es reconocido por GrapesJS, porque no existe el ID de estilos generados automáticamente de GrapesJS ni el atributo 'data-gjs-type='text''.
- ❌ 'getHTML' y 'getJSON' para guardar modificaciones no incluyen el nodo 'span' __styles__.

Para resolver este problema manualmente:
1. Después de añadir el nodo 'span' con sus estilos, haz doble clic en el componente GrapesJS seleccionado actualmente.
2. Haz clic en el cuerpo del 'lienzo' del iframe.
3. Haz doble clic en el componente GrapesJS seleccionado previamente.
4. El nodo 'span' se añade oficialmente a GrapesJS, con un ID de estilos auto-generado y el atributo 'data-gjs-type='text''.
bgrand-ch11 de enero de 2021

i️ No funciona directamente con HTML, solo funciona con JSON.

Component = nodo JSON (elemento html + GrapesJS data)

'''js addStylesToText (styles, tagName = 'span') { const inlineStyles = styles.map(style => style.join(':')).join(';') + ';' const selectedComponent = this.editor.getSelected() const components = selectedComponent.components().models

para (componente const de componentes) { const content = component.get('content')

if (contenido !== this.selection.anchorText) { continúa }

const componentId = component.index() const selectedText = content.substr( this.selection.rangeStart, this.selection.rangeEnd - this.selection.rangeStart ) const [prevHermano, siguienteHermana] = content.split(selectedText)

selectedComponent.append({ Tipo: 'textnode', contenido: prevHermano }, { at: componentId })

selectedComponent.append({ Tipo: 'textnode', contenido: siguienteHermano }, { at: componentId + 2 })

component.replaceWith({ etiquetaNombre: etiquetaNombre, Tipo: 'texto', atributos: { estilo: en línea Estilos }, componentes: [{ Tipo: 'textnode', contenido: selectedText }] })

console.log('addStylesToText()', { selectedComponent, componente, selectedText, Hermano prev, siguienteHermano })

Pausa } }


Fuentes:
- https://grapesjs.com/docs/modules/Components.html
- https://grapesjs.com/docs/api/component.html
ClaudeCode17 de mayo de 2026

Gracias por informar de esto, @bgrand-ch.

Buena pregunta sobre ¿Cómo actualizar/volver a renderizar después de añadir un nuevo nodo?. El enfoque recomendado con ProseMirror es usar la API orientada a eventos.

Empieza aquí:

  1. Consulta la documentación de GrapesJS de tu módulo específico
  2. Busca el método del oyente de eventos 'on()'
  3. La mayoría de las operaciones se pueden realizar escuchando eventos del editor y de los componentes

Patrones comunes: '''javascript Prestad atención a los cambios editor.on('Change', () => console.log('Something Changed'));

Ciclo de vida de los componentes editor.on('component:mount', (c) => console.log('component ready', c)); editor.on('component:update', (c) => console.log('component updated', c));


**Si sigues atascado:**
- Compartir una reproducción mínima de CodeSandbox
- Incluye lo que ya has probado
- Menciona tu versión GrapesJS
- ¡La comunidad está aquí para ayudar!

Preguntas y respuestas relacionadas

Continúa investigando con debates sobre temas similares.

Plugins de pago que cumplen con este problema

Seleccionado por temas clave y relevancia de etiquetas para ayudarte a enviar más rápido.

Ver todos los plugins

Cargando recomendaciones de plugins de pago...

Opción gratuita

Consulta los plugins de código abierto de GrapesJS en GitHub O haz una búsqueda rápida en nuestro catálogo gratuito.

Explora plugins gratuitos →
Opción premium

Los plugins premium incluyen soporte, actualizaciones regulares y funciones listas para producción — ahorrando días de trabajo de integración.

Explora plugins premium →

Tutoriales relacionados

Guías detalladas sobre el mismo tema.

Todos los tutoriales →

Explorar categorías de plugins

Ve directamente a las páginas de categorías de plugins en el marketplace.