How to refresh/re-render after new node added?
Question
I manually add a new node into a rendered component.
The visual result is OK, but impossible to save, because my style isn't with the auto-generated GrapesJS ID.
To manually resolve this poblem, I double-click on the component to enter to it and I click outside in the canvas body. Then, the GrapesJS ID and data-gjs-type='text' are added to the new node, and save is possible.
Vue.js:
A part of Main.vue file:
// Text Editor
// 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 = new TextEditorClass({
propsData: {
editor: this.editor
}
})
textEditor.$mount()
editorToolbar.innerHTML = ''
editorToolbar.appendChild(textEditor.$el)
editorToolbar.classList.replace('gjs-one-bg', 'bg-primary')
A part of TextEditor.vue file:
methods: {
// OK, no problem
onButtonClick (name) {
const { rte } = this.getRteData()
rte.exec(name)
console.log('onButtonClick()', { name, rte })
},
// Problem, no refresh/re-render after spanNode added
onFontValidate () {
const { rte } = this.getRteData()
let anchorNode = {}
this.selection.childNodes.forEach(childNode => {
if (childNode.textContent !== this.selection.anchorText) {
return
}
anchorNode = childNode
})
const spanNode = document.createElement('span')
const range = new Range()
// spanNode.setAttribute('data-gjs-type', 'text')
spanNode.style.fontFamily = 'Courier New'
range.setStart(anchorNode, this.selection.rangeStart)
range.setEnd(anchorNode, this.selection.rangeEnd)
range.surroundContents(spanNode)
// rte.selection().removeAllRanges()
rte.selection().addRange(range)
// this.editor.runCommand('core:component-exit')
console.log('onFontValidate()', {
rte,
range,
selected: this.editor.getSelected()
})
},
onMenuShow () {
const {
rte,
selection,
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,
rangeStart: this.selection.rangeStart,
rangeEnd: this.selection.rangeEnd
})
},
getRteData () {
const selected = this.editor.getSelected()
const rte = selected.view.activeRte
const selection = rte.selection()
const anchorNode = selection.anchorNode
return {
rte,
selection,
anchorNode
}
}
}
Answers (2)
Optimized TextEditor.vue file:
// ...
onFontValidate () {
const { rte } = this.getRteData()
const spanNode = document.createElement('span')
let anchorNode = {}
this.selection.childNodes.forEach(childNode => {
console.log({ childNode })
if (childNode.textContent !== this.selection.anchorText) {
return
}
anchorNode = childNode
})
rte.selection().setBaseAndExtent(
anchorNode, this.selection.rangeStart,
anchorNode, this.selection.rangeEnd
)
spanNode.style.fontFamily = 'Courier New'
spanNode.style.color = 'red'
spanNode.innerText = rte.selection().getRangeAt(0).toString()
rte.insertHTML(spanNode.outerHTML)
console.log('onFontValidate()', {
rte,
anchorNode,
spanNode
})
// const range = new Range()
// range.setStart(anchorNode, this.selection.rangeStart)
// range.setEnd(anchorNode, this.selection.rangeEnd)
// range.surroundContents(spanNode)
// rte.selection().removeAllRanges()
// rte.selection().addRange(range)
}
// ....
But the problem persist:
- ✔️
spannode replaces the previous content of selection, withfont-familyandcolorstyles. - ❌
spannode isn't recognized by GrapesJS, because there isn't GrapesJS auto-generated styles ID anddata-gjs-type='text'attribute. - ❌
getHTMLandgetJSONto save modifications doesn't includespannode styles.
To resolve manually this problem:
- After
spannode is added with its styles, double-click on the current selected GrapesJS component. - Click on the iframe
canvasbody. - Double-click on the previously selected GrapesJS component.
spannode is officially added to GrapesJS, with an auto-generated styles ID anddata-gjs-type='text'attribute.
ℹ️ Don't works directly with HTML, works only with JSON.
Component = JSON node (html element + GrapesJS data)
addStylesToText (styles, tagName = 'span') {
const inlineStyles = styles.map(style => style.join(':')).join(';') + ';'
const selectedComponent = this.editor.getSelected()
const components = selectedComponent.components().models
for (const component of components) {
const content = component.get('content')
if (content !== this.selection.anchorText) {
continue
}
const componentId = component.index()
const selectedText = content.substr(
this.selection.rangeStart,
this.selection.rangeEnd - this.selection.rangeStart
)
const [prevSibling, nextSibling] = content.split(selectedText)
selectedComponent.append({
type: 'textnode',
content: prevSibling
}, {
at: componentId
})
selectedComponent.append({
type: 'textnode',
content: nextSibling
}, {
at: componentId + 2
})
component.replaceWith({
tagName: tagName,
type: 'text',
attributes: {
style: inlineStyles
},
components: [{
type: 'textnode',
content: selectedText
}]
})
console.log('addStylesToText()', {
selectedComponent,
component,
selectedText,
prevSibling,
nextSibling
})
break
}
}
Sources:
Related Questions and Answers
Continue research with similar issue discussions.
Issue #913
[Question] Using Style tag instead of css
Hi, I've been taking a look inside of the dom_components, but cant seem to figure out how to manually override the styling aspect. I need a...
Issue #1982
[QUESTIONS] addcomponent after the selected component
how do I make it possible to add new components after the component I selected, at this time I use editor.DomComponents.addComponent (block...
Issue #2770
[QUESTION] Set selected element
Hello, is there a way to select an element after added to the body? When i add a link, i want to open the Component Settings immediately. H...
Issue #1912
[QUESTION] Custom component default children
Hi, I am trying to understand how I can achieve the following.Create a component with a default content as children.Create a block to add t...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.