Issue #2831💬 AnsweredOpened June 15, 2020by mustahsanmustafa1 reactions

Text components don't behave correctly after creating custom components

快速解答by artf1

You're mixing the old API with the new one for the custom component definition. With model: defaultModel.extend({ was required an inner static isComponent (you're using one outside, like in the new API) and without it, you're breaking the Component Type Stack You should just change model: defaultModel.extend({...}) to...

Read full answer below ↓

Question

Hi artf,

After creating custom components, the text component does not behave like before. It removes all the naked nodes after changing some code in import modal. e.g

<div>Insert your text 
        <a data-cke-saved-href="http://abc" href="http://abc">here</a>
</div>

After editing something in import modal it gets change to:

<div>
        <a data-cke-saved-href="http://abc" href="http://abc">here</a>
</div>

As you can see it removed the Naked text node i.e Insert your text. It should be noted that this behavior doesnt happen when there is no custom component added to the editor.

An example of custom component that i created:

export const NavbarMenu = editor => {
	var defaultType = editor.DomComponents.getType("default");
	var defaultModel = defaultType.model;
	editor.DomComponents.addType('menu-nav', {
		isComponent: el => el.tagName === 'menu-nav',
		model: defaultModel.extend({
			// Extend default properties
			defaults: Object.assign({}, defaultModel.prototype.defaults, {
				tagName: 'menu-nav',
				removable: true,
				copyable: false,
				droppable: false,
                resizable: false,
				editable: false,
				traits: [
                    {
                        type: 'text',
                        name: 'menuName',
                        label: 'Menu Name'
                    }
                ],
				attributes: {
                    'menuName': 'Header Nav'
				},
				propagate: ['removable', 'droppable', 'copyable', 'editable'],
				components: `
<div data-gjs-selectable="false" class="container-fluid bg-menu">
  <div class="row" data-gjs-selectable="false">
    <div class="col-lg-12" data-gjs-selectable="false">
      <div class="main-nav-center" data-gjs-selectable="false">
        <nav id="main-nav" class="prvidr-navbar-menu text-center" data-gjs-selectable="false">
          <ul id="main-menu" class="sm pixelstrap sm-horizontal catch-menu" data-gjs-selectable="false">
            <li data-gjs-selectable="false">
              <a data-gjs-selectable="false" href="javascript:void(0)">
                Menu Item 1
              </a>
            </li>
            <li data-gjs-selectable="false">
              <a data-gjs-selectable="false" href="javascript:void(0)">
                Menu Item 2
              </a>
            </li>
            <li data-gjs-selectable="false">
              <a data-gjs-selectable="false" href="javascript:void(0)">
                Menu Item 3
              </a>
            </li>
            <li data-gjs-selectable="false">
              <a data-gjs-selectable="false" href="javascript:void(0)">
                Menu Item 4
              </a>
            </li>
          </ul>
        </nav>
      </div>
    </div>
  </div>
</div>
				`
			}),
			init(){
				var props = this.getAttributes().props;
				if (props) {
					props = JSON.parse(props);
					this.addAttributes({'menuName':props['menuName']});
				}
			},
			toHTML: function () {
			    let props:any = {};
			    props['menuName'] = this.getAttributes().menuName;
				return `<menu-nav data-gjs-type="menu-nav" props='${JSON.stringify(props)}'></menu-nav>`
			}

		})
	});

	editor.BlockManager.add('menu-nav', {
		label: 'Header Menu',
		attributes: {
			class: 'fa fa-bars'
		},
		category: {
			label: "Dynamic Menu"
		},
		content: {
			type: 'menu-nav'
		}
	});
}

Answers (2)

artfJune 18, 2020

You're mixing the old API with the new one for the custom component definition. With model: defaultModel.extend({ was required an inner static isComponent (you're using one outside, like in the new API) and without it, you're breaking the Component Type Stack

You should just change model: defaultModel.extend({...}) to model: {...} but in your case, I'd also suggest reading carefully https://grapesjs.com/docs/modules/Components.html

mustahsanmustafaJune 15, 2020

UPDATE:

If i just extend my custom components from textnode instead of default, then the issue gets resolved somehow:

export const NavbarMenu = editor => {
	var defaultType = editor.DomComponents.getType("textnode"); //<-- changed to textnode
	var defaultModel = defaultType.model;
	editor.DomComponents.addType('menu-nav', {
		isComponent: el => el.tagName === 'menu-nav',
		model: defaultModel.extend({
                ......

but it will be better if the issue is resolved at default as well. cheers

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...

Free option

Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.

Browse free plugins →
Premium option

Premium plugins ship with support, regular updates, and production-ready features — save days of integration work.

Browse premium plugins →

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.