Checkbox trait value is rendered differently in view (render()) vs model (getHtml())
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...
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.