[QUESTION] Atomic blocks or complex components
Question
Hi,
I'm studying the editor and I think I understand the difference between components and blocks. Now, I was wondering if GrapesJS offers the possibily to manage a block as an atomic entity or permits to define components as a tree with of nodes.
For example, I have a block called separator that basically is a table with a tbody, a tr and a td that are all components in gjs. I would like that the click on any component of this separator block in reality selects the table component at which I setup custom traits and properties that I then can split programmatically on the correct component of the block (e.g. setting the height on the table would in reality set the height of the nested td).
My aim is to give the users the feeling that they are dealing with a single atomic element even if at the source code level the properties they set for that element are distributed among the different nodes that compose that element.
The main issue that I can foresee is that the source code should be importable so as it is generated by the editor and I'm not sure if the editor can distinguish the separator element from any td component of a table component used for tabular data and if it can sniff all the styles to repopulate the traits and the properties in the editor.
Is such a thing possible?
Thank you.
Answers (3)
The main issue that I can foresee is that the source code should be importable so as it is generated by the editor and I'm not sure if the editor can distinguish the separator element from any td component of a table component used for tabular data and if it can sniff all the styles to repopulate the traits and the properties in the editor.
Yeah this is a good question, basically, the editor has a stack of component types and every time you add a new one it's placed on top of the stack
[
youLastAddedComponent,
...
table,
image,
text,
default,
]
Let's say you need to add a table component inside the selected one (Component's API)
const selected = editor.getSelected();
// selected is the Component's model
selected.append(`<table>...</table>`);
Now when you pass the HTML, the string is parsed and each Node is translated in a component object (at the moment, just a JS object) and once appended to selected's inner components (selected.components().add({ ...component object... })) it becomes the Component. But to distinguish exactly which type of model is the Node, you iterate over the stack, execute on each type isComponent static method and check if it's valid (you generally return an object like { type: 'my-type', ... })
So isComponent method here is the key, there you can implement the correct logic to make the editor understand if it's a normal table or the separator one (the method takes the Node as the argument so you just use normal DOM's API for the logic).
You can also skip the recognition part by setting the type explicitly, in this way:
<table data-gjs-type="separator">...</table>
Be sure to check also the guide on how to correctly store and load templates, because if you use the same HTML, stored from the editor, for the editing, it's WRONG, you have to use the JSON data (which already contains all the info about your types, so you just skip the parsing and the recognition).
the fact that it has returned a type for this node prevents the editor from testing the inner nodes of this node for a component type?
No, inner nodes will be still traversed, but you can actually prevent that with this as a return to your isComponent
isComponent(el) {
...
return {
type: 'separator',
// If the returned object contains `components`,
// inner nodes will be skipped
components: [],
}
}
but, with this current setting you're telling that you have no components, so you'd actually get this rendered in you canvas:
<table ...></table>
empty table, so what you can do?!? With this:
components: [el.innerHTML],
it's the same like not putting components, because the string will be parsed and processed again. What you can actually do, what I think might be your case:
return {
type: 'separator',
content: el.innerHTML,
components: [],
}
content is for static stuff, the editor won't create Components and you won't even be able to select them (not being a Component you don't have the model)
Yeah, the content property could really solve my problem but I guess I have to manually keep it's value in sync (with string manipulation) with the changes made by the users to the properties and the traits of the separator component, and, since it is part of the model of a separator component, any change to it will trigger a view update, right?
Well as you said I would like that the click on any component of this separator block in reality selects the table I supposed that the separator was something static, without the need to change inner stuff, it depends what your traits are doing, eg. if you just change attributes of the component itself it's still in sync with its view
Otherwise, if I let the inner nodes to be traversed but check the ancestors and/or the descendants to establish if e.g. the they are part of my separator component and make them not droppable, not draggable and not selectable (maybe defining separated components like separator-tr, separator-td, etc.)? Could this be a better solution?
Yeah sure
Related Questions and Answers
Continue research with similar issue discussions.
Issue #2061
[QUESTION] How to place a component at the end of the canvas.
How can I put my footer at the end of the canvas? I want the editor to not allow placing a block/widget below the footer. example
Issue #792
Script property using function don't work with blocks
In Components & JS docs there's this example: This works perfectly. But I couldn't use a function instead of a string in script, as is said...
Issue #1875
[QUESTION] ID changes when importing html or passing html in components prop in config.
Hi, I built a component and block that I can drag and drop in the canvas. My issue is that some styles rely on the ID. I know this might no...
Issue #1960
[QUESTION] Dynamic class generation with a template
Hi there ! I am wondering if there is a way to force the dynamic class generation (of the form cXXXXX which happens when I drag'n'drop a co...
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.