GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.36k stars 4.05k forks source link

BUG: Loading `component` and `styles` from `pageManager` #5460

Closed mapsmarketing closed 12 months ago

mapsmarketing commented 1 year ago

GrapesJS version

What browser are you using?

Chrome v117.0.5938.149

Reproducible demo link

https://jsfiddle.net/9oLrsvya/

Describe the bug

How to reproduce the bug?

  1. Please see the pageManager object in the jsfiddle

What is the expected behavior? Looking at the following documentation: https://grapesjs.com/docs/api/pages.html#pages

It should load in the component and styles from the pageManager correctly into the editor.

What is the current behavior? When you check the code via "view code" in the editor you'll notice neither the exact HTML neither the styles have correctly loaded. The body tag does not retain the ID tag that the editor adds when applying any kind of styling to it which means the styling won't work. And the CSS hasn't loaded in correctly at all.


If it is a confirmed bug, I'd be happy to attempt to fix it. It would be great to roughly know where the component and styles are being loaded from as I am not intimately familiar with the project files.

Code of Conduct

artf commented 12 months ago

Hi @mapsmarketing currently GrapesJS is not able to properly import HTML documents as string, so the parser skips elements like html, head, body and returns only what is parsed inside the body, one workaround you could do right now is to pass component as object

component: {
  attributes: { id: 'ignt' },
  components: '<div class="my-el">Testing world!</div>',
},

Regarding the styles, it's the browser's default CSSOM parser issue, please refer to this plugin

mapsmarketing commented 12 months ago

Hi @artf ,

I've found a way to get the body attributes along with the styles saving correctly via the following method:

const pageManager = editor.Pages;
const page = pageManager.get('my-page-id').toJSON();
const component = pageManager.get('my-page-id').getMainComponent();
page.styles = editor.getCss({ component });

I noticed that your Page.toJSON outputs everything of the specified page. However, it doesn't seem to output the styles correctly (it's always set as undefined for me), so I retrieve the styles for the active page via the editor and override the ones in the JSON. I believe styles are not set due to the if (!props.frames) check you have in Page.ts

The below is an example of how I'm loading in the HTML. I am using frames since component doesn't work with the supplied structure. I am loading in the styles without the use of the parser plugin and it seems to be loading in without issues.

const pageJSON = {...}; // Contains the `frames` and `styles`
const editor = grapesjs.init({
    pageManager: {
        pages: [
            {
                id: 'my-page-id',
                frames: pageJSON.frames,
                styles: pageJSON.styles,
            }
        ]
    }
});

I also noticed that the PageManager.select function doesn't update styles when switching between the pages which forces me to run: editor.setStyle(pageJSON.styles);

mapsmarketing commented 12 months ago

One annoying thing is that the editor is prepending the below default styles which when saving the styles has numerous duplicates of it:

* {
  box-sizing: border-box;
}
body {
  margin: 0;
}

Edit: Looking at the docs I finally managed to find protectedCss and when set to empty stops the styles being prepended.

artf commented 11 months ago

I'd suggest you following this to properly load your JSON projects. Styles are stored in a separate module, that's the reason why you don't see them per pages.

mapsmarketing commented 11 months ago

Hi @artf ,

I saw that in your docs. I am storing the pages separately in my database at the moment which is why I was approaching it in the way I have.

I might abandon this and try and make it work with your Storage Manager instead in some way.

Have an awesome weekend Artf

mapsmarketing commented 10 months ago

I've managed to get the HTML and CSS for each page correctly and save it to the DB without the use of storeManager. I just didn't like the way storeManager did things.

Here is how I'm putting it together (this also saves/loads the <body> correctly):

const editorPages = [
  {
    id: id,
    name: title,
    frames: frames, // i'm not using `component`
    styles: styles,
  }
];

editorRef.current.Commands.add("savePage", async () => {
  const pageManager = editorRef.current.Pages;
  const selectedPage = pageManager.getSelected();
  const component = selectedPage.getMainComponent();
  const selectedPageJSON = selectedPage.toJSON();
  const Html = editorRef.current.getHtml({
    component: component,
    cleanId: true,
  });
  const Css = editorRef.current.getCss({
    component: component,
    avoidProtected: true,
  });

  pageManager.getAll().map((item) => {
    if (item.getId() === selectedPage.getId()) {
      item.set("styles", Css);
      item.set("frames", selectedPageJSON.frames);

      pageManager.add(item);
    }
  });

  dispatch(
    updatePage({
      _id: selectedPage.getId(),
      frames: selectedPageJSON.frames,
      component: Html, // saving it, but not using it
      styles: Css,
    })
  );
});

I'm setting avoidProtected to true as otherwise it infinitely continues to append the default styling to the CSS.

Here is my init purely for completions sake:

editorRef.current = grapesjs.init({
  autoload: true,
  container: `#editor`,
  plugins: [
    grapesjsPresetWebPage,
    grapesjsParserPostCSS,
    grapesjsPluginsBlocksBasic,
    grapesjsPluginsCKEditor,
  ],
  pluginsOpts: {
    [grapesjsPresetWebPage]: {},
    [grapesjsParserPostCSS]: {},
    [grapesjsPluginsBlocksBasic]: {},
    [grapesjsPluginsCKEditor]: {},
  },
  selectorManager: { componentFirst: true },
  storageManager: false,
  pageManager: {
    pages: editorPages,
  },
});

The styles need to be re-set when navigating between pages to maintain any new changes:

const pageManager = editorRef.current.Pages;
pageManager.select(page._id);
editorRef.current.setStyle(page.styles);