Checkbox trait value is rendered differently in view (render()) vs model (getHtml())
@mararn1618 can you create a reproducible live demo of this issue, please?
Read full answer below βQuestion
I have a custom component with a checkbox trait 'show-name'. When the trait value is 'true', the component renders in the view to the attribute show-name="true", whereas the model only has "show-name" and renders to "show-name" via getHtml().
How can I get a checkbox-trait to render to "show-name=true" via getHtml()?
(Sorry for the long reference code below ;-) )
View attribute is show-name="true"
<filter-wizard data-gjs-type="Feature (Enum)" filter-model="filterDAO.getFilterForPropertyProfileId(46)" pp-id="46" pp-type="4" show-name="true" data-highlightable="1" class="gjs-comp-selected" style="width: 100%; display: block; padding: 16px; min-height: 160px; background-size: 60%; background-image: url("assets/feature_Enum_opa.png"); background-repeat: no-repeat; background-position: center center;"><center><label>Interfaces</label></center></filter-wizard>
Model attribute is only show-name, but not "true" (div is the same element as filter-wizard in view)
<div class="row p-l-15 p-r-15 p-t-30 p-b-30 dpt-text-accent-dark ng-isolate-scope" filter-model="filterDAO.getFilterForPropertyProfileId(46)" pp-id="46" pp-type="4" show-name></div>
### JSON Model does only contain show-name, but not "true"
`{"gjs-assets":"[]","gjs-css":"* { box-sizing: border-box; } body {margin: 0;}","gjs-styles":"[]","gjs-html":"<filter-wizard filter-model="filterDAO.getFilterForPropertyProfileId(46)" pp-id="46" pp-type="4" show-name></filter-wizard>","gjs-components":"[{"tagName":"filter-wizard","type":"Feature (Enum)","name":"","removable":true,"draggable":true,"droppable":false,"badgable":true,"stylable":true,"stylable-require":"","unstylable":"","highlightable":true,"copyable":true,"resizable":false,"editable":false,"layerable":true,"selectable":true,"hoverable":true,"void":false,"state":"","status":"","content":"","icon":"","style":"","attributes":{"filter-model":"filterDAO.getFilterForPropertyProfileId(46)","pp-id":"46","pp-type":"4","show-name":true},"classes":[],"script":"","traits":[{"type":"checkbox","label":"Show Feature Name!","name":"show-name","min":"","max":"","unit":"","step":1,"value":true,"default":"","placeholder":"","changeProp":0,"options":[]},{"type":"select","label":"Alignment","name":"align","min":"","max":"","unit":"","step":1,"value":"","default":"","placeholder":"","changeProp":0,"options":[{"value":"'left'","name":"Left"},{"value":"'center'","name":"Center"},{"value":"'right'","name":"Right"}]},{"type":"string","label":"Replace label any with ..","name":"label-any","min":"","max":"","unit":"","step":1,"value":"","default":"","placeholder":"","changeProp":0,"options":[]},{"type":"number","label":"Force x columns for selection table","name":"enum-columns","min":"","max":"","unit":"","step":1,"value":"","default":"","placeholder":"","changeProp":0,"options":[]},{"type":"string","label":"Replace label Irrelevant with ..","name":"label-irrelevant","min":"","max":"","unit":"","step":1,"value":"","default":"","placeholder":"","changeProp":0,"options":[]}],"propagate":"","showName":true,"components":[],"open":false}]"}`
### Component Code (Typescript)
`
class PPBlock {
id: string;
pp: propertyProfileModel;
label: string;
content: string;
category: string;
attributes: Object;
constructor(pp: propertyProfileModel) {
this.id = 'pp-filter-' + pp.id;
this.pp = pp;
this.label = pp.name;
this.content = "";
this.category = "Features";
if(pp.type == ET.ET_PPType.Integer || pp.type == ET.ET_PPType.Float) {
this.label = svg_slider_two_handles + '<br/>' + this.label;
}
if(pp.type == ET.ET_PPType.Intervall) {
this.label = svg_slider + '<br/>' + this.label;
}
if(pp.type == ET.ET_PPType.String)
this.attributes = { class: 'fa fa-i-cursor' };
if(pp.type == ET.ET_PPType.Enum || pp.type == ET.ET_PPType.Boolean)
this.attributes = { class: 'fa fa-th' };
this.buildContent();
}
buildContent(): void {
this.content = '<filter-wizard ' +
'filter-model="filterDAO.getFilterForPropertyProfileId(' + this.pp.id + ')" ' +
'pp-id="' + this.pp.id + '"' +//used in view by component to load more information about the propertyprofile
'pp-type="'+ this.pp.type +'"' + //used to match against the right component
//'pp-name="' + this.pp.name + //only used in view to show name of component
'>' +
'</filter-wizard>';
}
}
class PPComponent {
/* real data of component */
tagName: string = "filter-wizard";
type: string;//is generated in constructor from ppType
model: any;
view: any;
attributes: Object;
ppType: number;//ET.ET_PPType
ppTypePrinted: string;
propertyProfileModelService: propertyProfileModelService;
getView(): any { return this.view; }
getModel(): any { return this.model; }
constructor(editor: any, ppType: number, ppTypePrinted: string, propertyProfileModelService: propertyProfileModelService) {//ET.ET_PPType
let defaultComponentType = editor.DomComponents.getType('default');
let defaultModel = defaultComponentType.model;
let defaultView = defaultComponentType.view;
let __this = this;
this.propertyProfileModelService = propertyProfileModelService;
this.attributes = {};
this.ppType = ppType;
this.ppTypePrinted = ppTypePrinted;
this.type = "Feature (" + ppTypePrinted + ")";
/* build model */
let modelDefaults: Object = this.helper_mergeobjects(defaultModel.prototype.defaults,
{
showName: true,
traits: this.buildTraits(),
droppable: false
/*draggable: boolean = true;
removable: boolean = true;
droppable: boolean = false;
stylable: boolean = false;
copyable: boolean = false;
*/
}
);
this.model = defaultModel.extend(
{/* extends parameter 1 */
defaults: modelDefaults
},
/* extends parameter 2, static functions */
{
isComponent(element: any): Object {
if(null == element || typeof element == 'undefined')
return;
if(typeof element.tagName == 'undefined')
return;
if(element.tagName.toUpperCase() != __this.tagName.toUpperCase())//element is injected in uppercase version from grapesjs for magical reasons
return;
let ppType_from_element: number = Number(element.attributes['pp-type'].nodeValue);
if(ppType_from_element != __this.ppType)
return;
return {type: __this.type};
}
}
);
/* build view */
this.view = defaultView;
let defaultRender: Function = this.view.prototype.render;
this.view = defaultView.extend(
{
//tagName: 'div',
model: this.model,
init: function() {
this.listenTo(this.model, 'change:attributes', this.render);
},
render: function () {
let view = this;//hack: this now does NOT refer to the instance of class, but is a view
defaultView.prototype.render.apply(view);
//view.prototype.render.apply(view,null);
view.el.style.width = "100%";//needed?!
view.el.style.display = "block";//make full width
view.el.style.padding = "16px";//needed?!
//view.el.style.minHeight = "240px";
view.el.style.minHeight = "160px";
view.el.style.backgroundSize = "60%";
view.el.style.backgroundImage = "url('assets/feature_" + __this.ppTypePrinted + "_opa.png')";
view.el.style.backgroundRepeat = "no-repeat";
view.el.style.backgroundPosition = "center";
//build inner HTML
view.el.innerHTML = "";
let ppid: number = Number(view.attr["pp-id"]);
if(null != ppid) {
let pp: propertyProfileModel = __this.propertyProfileModelService.getPPById(ppid);
if(null != pp)
view.el.innerHTML += "<center><label>" + pp.name + "</label></center>";
}
/*does not work
try {
let ppTitle: string = view.el[0].attributes['pp-name'].nodeValue;
if(null != ppTitle)
view.el.innerHTML = "<center>" + ppTitle + "</center>";
} catch(e) {}
*/
return view;
}
}
);
}
buildTraits(): Array<Object> {
let ret = [];
ret.push(
{
type: 'checkbox',
label: 'Show Feature Name!',
name: 'show-name',
value: 'true',
//changeProp: 0
}
);
ret.push(
{
type: 'select',
label: 'Alignment',
name: 'align',
options: [
{value: '\'left\'', name: 'Left'},
{value: '\'center\'', name: 'Center'},
{value: '\'right\'', name: 'Right'}
],
//changeProp: 0
}
);
if(this.ppType == ET.ET_PPType.Enum) {
ret.push(
{//trait 3
type: 'string',
label: 'Replace label any with ..',
name: 'label-any',
//changeProp: 0
}
);
ret.push(
{//trait 3
type: 'number',
label: 'Force x columns for selection table',
name: 'enum-columns',
//changeProp: 0
}
);
}
if(this.ppType == ET.ET_PPType.Boolean) {
ret.push(
{
type: 'string',
label: 'Replace label TRUE with ..',
name: 'label-true',
//changeProp: 0
}
);
ret.push(
{
type: 'string',
label: 'Replace label FALSE with ..',
name: 'label-false',
//changeProp: 0
}
);
}
if(this.ppType == ET.ET_PPType.Enum || this.ppType == ET.ET_PPType.Boolean) {
ret.push(
{//trait 3
type: 'string',
label: 'Replace label Irrelevant with ..',
name: 'label-irrelevant',
//changeProp: 0
}
);
}
return ret;
}
helper_mergeobjects(a: Object, b: Object): Object {
let ret: Object = {};
for (var key in a)
if (a.hasOwnProperty(key)) ret[key] = a[key];
for (var key in b)
if (b.hasOwnProperty(key)) ret[key] = b[key];
return ret;
}
}
`Answers (3)
@mararn1618 can you create a reproducible live demo of this issue, please?
Hi @artf, of course. I have extracted the relevant parts of my project and created a demo along with reproduction steps:
- Demo: https://www.secretbakery.io/grapesjs/issue_1424.html
- List demo files: https://www.secretbakery.io/grapesjs/
Thanks for taking care!
Edit: I have added a downloadable archive of the demo to the files (grapesjs_1424.zip).
I am right now using a workaround for this, so that my AngularJS directive - where this attribute shall be used - recalculates 'show-name' as if we had 'show-name="true"' in the link stage.
Workaround AngularJS Directive
class filterWizardDirective implements ng.IDirective {
/* .. code ..*/
link($scope: any, element: any, attrs: any) {
//explicitly calculate <filter-wizard show-name>
if(null == $scope.showName || 'undefined' == typeof $scope.showName || "" == $scope.showName) {
$scope.showName = 'showName' in attrs && "" == attrs.showName;
}
}
scope: Object = {
showName: "=?",
/* .. code ..*/
};
}
Related Questions and Answers
Continue research with similar issue discussions.
Issue #1198
QUESTIONS : Find element in Model
I have custom component and it render below html, I want to update value of h4 tilte and other element on trait changed, but not sure how c...
Issue #1679
How to render component in canvas using trait.
Hi Artf ,Thanks for your great tool,I have created test case for my problem https://js.do/code/271771 I have created custom tool sidebar an...
Issue #1227
Re-render view in editor's canvas whenever Trait value changes
Is there any possible way in grapesjs that whenever a user changes the value of a Trait of a custom component the render function should li...
Issue #1658
[BUG] Checkbox trait does not load as checked even when the trait value is true
Hi, I'm using Grapesjs version 0.14.33. I'm loading some components when the form loads and I have a component with a checkbox trait. When...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.
Browse free plugins βPremium plugins ship with support, regular updates, and production-ready features β save days of integration work.
Browse premium plugins βRelated tutorials
In-depth guides on the same topic.
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.