BUG: Class modifications of components are not automatically detected
Question
GrapesJS version
- I confirm to use the latest version of GrapesJS
What browser are you using?
Chrome Version 116
Reproducible demo link
https://jsfiddle.net/h5b0ndvL/1/
Describe the bug
When a component is re-rendered in the canvas, a new attribute added to the model definition get rendered, but a newly added class doesn't get added when the component is rendered in the canvas. The initialize function of Component.ts add the defaults attributes as well as the component's attribute.
this.set('attributes', { ...(result(this, 'defaults').attributes || {}), ...(this.get('attributes') || {}), });
However, when looking at the initClasses() function, I can only see the get('classes') from the component itself, but it doesn't compare with the default list ((result(this, 'defaults').classes) to check if it changed.
How to reproduce the bug?
- Create a component with predefined classes
- Drag the component on the page
- Save the page
- Change the classes definition of the component and add a new class to the model
- Change the attributes definition of the component and add a new attribute to the model
- Refresh the page to load the content again
What is the expected behavior? When a class/attributes for a component type is modified/added, the component must be re-rendered.
What is the current behavior? The newly class is not added to the model class list like it does for the attributes.
If is necessary to execute some code in order to reproduce the bug, paste it here below:
From the JsFiddle link, go to the blocks and drag the "Test Class" block to the canvas. Based on the original definition of the component, the class 'section' and the attributes 'data-type' should be added to the component:
I then add a new class and a new attribute to the component's model definition. After running the code again, the new attribute gets added to the component, but the new class doesn't get added to the component:
Here's a video of how to reproduce it based on my JsFiddle Link: https://github.com/GrapesJS/grapesjs/assets/144697108/3da7d308-a8f2-4405-8e81-fa1644bb6c9e
Code of Conduct
- I agree to follow this project's Code of Conduct
Answers (3)
For the moment, as a workaround, I added this in a editor.on('component:mount', (component) => {}); function until any feedback is received. It seems to be working well. In a case where a class is removed from the definition, the removed class will still be available on the saved component's class list, but I guess there's no workaround around this.
editor.on('component:mount', (component) => {
const cls = component.getClasses() || [];
const clsArr = __isString(cls) ? cls.split(' ') : cls;
// Default Class
const dCls = component.defaults.classes || [];
// Temporary class array for comparison
const tClsArr = clsArr.map((cl) => __isString(cl) ? cl : cl.name);
// New class that is not in current tClsArray
const nClsArr = dCls.filter((x) => tClsArr.indexOf(x) === -1);
if(nClsArr.length > 0) {
// Found a missing class, add it to the component
component.addClass(nClsArr.toString());
}
function __isString(x) {
return typeof x === 'string';
}
});
For now, I have achieved my expected result, but any feedback would be appreciated. Thanks.
Yeah, this is intentional with classes, with your approach you won't be able to remove defined classes from the component, they will keep being added on component init.
Which is fine for when a class has been forgotten on a component and we want to add it in another version of the plugin on render. For us, we've set these classes to private, so they are not visible to the user and can't be removed manually.
I believe, as a workaround, I could store old classes definition to a new property and compare them before rendering the new class to see which one needs to be removed. I can work on a workaround that will work for us.
Thanks for your reply @artf,
Something like this in the initClasses() function of Component.ts would add the new classes to the element on render. Let me know what you think :
const cls = this.get('classes') || attrCls || [];
// changing from const to let
let clsArr = isString(cls) ? cls.split(' ') : cls;
/* Compare with default class */
const dCls = result(this, 'defaults').classes || [];
const tClsArr = clsArr.map((cl: any) => isString(cl) ? cl : cl.name);
const nClsArr = dCls.filter((x: any) => tClsArr.indexOf(x) === -1);
if(nClsArr.length > 0) {
clsArr = [].concat(clsArr, nClsArr);
}
this.stopListening(...toListen);
const classes = this.normalizeClasses(clsArr);
const selectors = new Selectors([]);
this.set('classes', selectors, opts);
selectors.add(classes);
selectors.on('add remove reset', this.__upSymbCls);
// Clear attributes from classes
attrCls && classes.length && this.set('attributes', restAttr);
// @ts-ignore
this.listenTo(...toListen);
return this;
}
Related Questions and Answers
Continue research with similar issue discussions.
Issue #4323
BUG: Performance issue while dragging component to canvas when you have 2k component on canvas
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? Google Chrome Version 101.0.4951.41 (Officia...
Issue #5990
BUG: When you delete a component, duplicate classes between components will be deleted.
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome 126.0.6478.127(Windows) Reproducibl...
Issue #6458
BUG: sector.setName doesn't work
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome Reproducible demo link https://jsfi...
Issue #6096
BUG: Resizable Box Disappears on Component Reselection
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome Reproducible demo link https://jsfi...
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.