Issue #1424Opened September 14, 2018by mararn16180 reactions

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(&quot;assets/feature_Enum_opa.png&quot;); 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>

Code SnippetTEXT
### 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)

artfSeptember 15, 20180 reactions

@mararn1618 can you create a reproducible live demo of this issue, please?

mararn1618September 23, 20180 reactions

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.

Paid Plugins That Match This Issue

Curated by issue keywords and label relevance to help you ship faster.

View all plugins

Loading paid plugin recommendations...

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.