Issue #1255Opened July 3, 2018by jdodsoncollins2 reactions

[QUESTION]: How to set editor html contents from custom component?

Question

I'm having trouble getting a custom block+component to display its template HTML inside the grapes editor. Within the exported HTML, the markup and CSS is generated as expected.

Within the editor though, the block doesn't leave behind any sort of UI (though it is dragging in successfully based on the exported html/css)

My question is: What is the proper way to have the 'toHtml' method output also apply to the editor iframe? Based on similar examples I see elsewhere, it seems like in every other case it has to be generated from the block content property. However, for reasons I'm omitting here, I have to use the object format for the content in order to get the functionality provided by custom components

Code:

block

    const bm = editor.BlockManager;
    bm.add('flex-columns', {
        label: '2 Columns',
        content: {
            type: 'columns'
        },
    });

component


export default (editor, config = {}) => {
    const domc = editor.DomComponents;
    const defaultType = domc.getType('default');
    const defaultModel = defaultType.model;
    const defaultView = defaultType.view;

    domc.addType('columns', {
        model: defaultType.model.extend({
            defaults: Object.assign({}, defaultType.model.prototype.defaults, {

                rowAttr: {
                    class: `row`,
                },

                colAttrMain: Object.assign({}, {
                    'data-gjs-draggable': `row`,
                }, {
                    class: `cell-main`
                }),

                colAttrAside: Object.assign({}, {
                    'data-gjs-draggable': `row`,
                }, {
                    class: `cell-main`
                }),

                styleRow: `
                .row {
                    display: flex;
                    justify-content: flex-start;
                    align-items: stretch;
                    flex-wrap: nowrap;
                    padding: 10px;
                }
                .row:first-child {
                    padding-right: 20px;
                }
                @media (max-width: 768px) {
                .row {
                    flex-wrap: wrap;
                    }
                }
                `,
                styleClm: `
                .cell-aside {
                    min-height: 75px;
                    flex: 1;
                }
                .cell-main {
                    min-height: 75px;
                    flex: 2;
                }`,
                template: '',
            }),

            attrsToString: function (attrs) {
                const result = [];

                for (let key in attrs) {
                    let value = attrs[key];
                    const toParse = value instanceof Array || value instanceof Object;
                    value = toParse ? JSON.stringify(value) : value;
                    result.push(`${key}=${toParse ? `'${value}'` : `"${value}"`}`);
                }

                return result.length ? ` ${result.join(' ')}` : '';
            },

            initialize(o, opt) {
                defaultType.model.prototype.initialize.apply(this, arguments);

                const attrsRow = this.attrsToString(this.attributes.rowAttr);
                const attrsCellAside = this.attrsToString(this.attributes.colAttrAside);
                const attrsCellMain = this.attrsToString(this.attributes.colAttrMain);

                const styles = 
                `<style>
                    ${this.attributes.styleRow}
                    ${this.attributes.styleClm}
                </style>`

                this.attributes.template = 
                `<div ${attrsRow}>
                    <div ${attrsCellAside}><p>Aside</p></div>
                    <div ${attrsCellMain}><p>Main</p></div>
                </div>` + styles;
            },

            toHTML: function () {
                if (!this.view || !this.view.el) {
                    let html = document.createElement('div');
                    const customRenderedEl = this.generateHtml();
                    html.appendChild(customRenderedEl);
                    console.log('html is', html);
                    return html.innerHTML;
                }
            },

            generateHtml(el = this.view.el) {
                let generatedSection = el;
                generatedSection.innerHTML = this.attributes.template;
                return generatedSection;
            },
        }, 
            {
            isComponent(el) {
                var result = '';
                if (el.tagName == 'DIV') {
                    result = {
                        type: 'columns'
                    };
                }
                return result;
                }
            }
        ),

        view: defaultType.view
    })
}

Within the HTML, here is what is shown:

<div class="row">
    <div class="cell">
      <p>Column
      </p>
    </div>
    <div class="cell">
      <p>Column
      </p>
    </div>
  </div>

, with nothing shown in the grapes editor iframe

Answers (3)

artfJuly 11, 20182 reactions

Well with this this.attributes.template = ... you do nothing to the view so it's just empty. You have to update the model so the view reacts to the change. Eg.

// inside the Model
// use init instead of initialize, so you avoid `defaultType.model.prototype...`
init () {
	// ...
	// if the component has no children
	if (!this.components().length) {
		this.components(`<div ${attrsRow}>
                    <div ${attrsCellAside}><p>Aside</p></div>
                    <div ${attrsCellMain}><p>Main</p></div>
                </div>${styles}`);
	}
}
no-response[bot]July 21, 20180 reactions

This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further.

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.