Issue #4654Opened October 11, 2022by malonecj0 reactions

BUG: Custom component for text node including special characters

Question

GrapesJS version

  • I confirm to use the latest version of GrapesJS

What browser are you using?

Chrome v105

Reproducible demo link

https://codesandbox.io/s/fancy-brook-ppyc8v?file=/index.js

Describe the bug

Background: I am trying to configure grapesjs to be able to edit markup that contains the Liquid template language. As such I would like to create two types of custom components.

  1. A template block in the form of {{some_variable}}
  2. A condition block in the form of {% if condition .... %}

The custom components should later be configured to allow the user to modify certain aspects of the components.

However, it appears that there is some issue in achieving the second requirement, having a code block that starts with {%}

How to reproduce the bug?

  1. Run the code sandbox link
  2. Observe that the custom component is initialised for the case of liquid-template-block but not for the case of liquid-condition-block

What is the expected behavior? the initialisers for both custom components should be run

What is the current behavior? only the initialiser for the custom component liquid-template-block is run

If is necessary to execute some code in order to reproduce the bug, paste it here below: just run the code sandbox link

Code of Conduct

  • I agree to follow this project's Code of Conduct

Answers (2)

artfOctober 20, 20220 reactions

Hi @malonecj I'm not sure how exactly you would expect to see and manage those components (especially when you combine templates inside conditions) but I think your current approach in isComponent is not properly correct as you have to deal with text nodes. Inside your liquid handle isComponent you use el.textContent which I think should be similar to liquid condition with el.nodeValue as it's only available on text nodes (probably it would be better to check if it's actually a text node el.nodeType === 3). As a first step try to update your custom components in this way, as currently are skipped from the parser due to this condition

editor.Components.addType("liquid-condition", {
    isComponent: (el) => {
        const { nodeValue } = el;
        const isComponent = nodeValue?.trim().includes("{%");
        // This probably will help you understand better how the parser processes nodes
        console.log('isComponent liquid-condition', { el, nodeValue, isComponent });

        if (isComponent) {
          return {
              tagName: 'liquid-condition-el',
              components: { type: 'textnode', content: nodeValue },
          }
        }
    },
    ...

editor.Components.addType("liquid-handle", {
    isComponent: (el) => {
        const { nodeValue } = el;
        const isComponent = nodeValue?.trim().includes("{{");
        console.log('isComponent liquid-handle', { el, nodeValue, isComponent });

        if (isComponent) {
          return {
              tagName: 'liquid-handle-el',
              components: { type: 'textnode', content: nodeValue },
          }
        }
    },
    ...

But I don't think this approach will work once you start to mix templates with conditions as from the HTML parser perspective you receive something like this:

* div
  * textNode - containing content starting from '{% case...' to '{% endcase %}'
  * div - containing 'Hello World!!!'
  * textNode - containing `{{ some_content }}`

So probably another approach would be to use one custom component as a parser of the template engine


Components.addType("liquid-case-condition", {
   // no need for isComponent here, only the parser will output components for the engine
    model: {
        toHTML(opts) {
            return `{% case ${this.get('conditionName')}  %}
                ${this.getInnerHTML(opts)}
            {% endcase %}`;
        }
    },
});

// ... other components to handle better the template engine

Components.addType("template-engine", {
    isComponent: (el) => {
        const { nodeValue } = el;

        if (nodeValue?.trim().includes("{%")) {
          const parsedTextNode = parseEngineText(nodeValue);
          // transform parsed stuff in components for the editor, eg.
          return {
              type: 'template-engine',
              components: [
                  {
                      type: 'liquid-case-condition',
                      conditionName: 'shop.subscription.currentPlanName',
                      components: [
                          // ... other template engine components
                      ]
                }
                // ...
              ]
          };
        }
      },
});
artfNovember 3, 20220 reactions

I'm closing this one as there are no issues with the core itself, if you have any other questions @malonecj let me know.

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.