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

Convert JSON to HTML server-side #3399

Closed AdamTorma closed 3 years ago

AdamTorma commented 3 years ago

So first of all, thank you @artf for this amazing project.

I have a question: Is it possible to easily convert the components-JSON to HTML on the server-side (in a Node.js environment)? If it is possible, can you give me a hint how to achieve this?

On the client-side it's quite straightforward: I can get the components-JSON (JSON.stringify(editor.getComponents());) and save it to my database. Then I can reuse my components later (editor.setComponents();) and get the HTML (editor.getHtml();) to show it in the browser. In my case though, I have to do it on the server-side.

Any help would be greatly appreciated!

Ju99ernaut commented 3 years ago

Maybe you could install grapesjs server-side(similar to client-side) and use it for rendering, otherwise you'll have to reverse engineer the renderer. Another alternative would be to store the generated HTML in the database as well.

artf commented 3 years ago

Well, I think that would be cool but unfortunately, at the moment, we leak a lot of DOM interfaces at the initialization level. So, it might be possible if you're ok with using something like jsdom to avoid breaking stuff due to DOM calls, otherwise, I'd be happy to merge a PR with the proper refactoring (eg. by adding a new headless option)

Detzler commented 3 years ago

+1

need ssr feature so much, can't live without

bgrand-ch commented 3 years ago

Hello,

For future questions or technical issues, which aren't bugs, GitHub's Discussions tab is the place to be.

Don't forget to close this issue if it is resolved or write a new detailed message in Discussions -> Q&A category (and close this issue).

Thank you for your understanding.

anlumo commented 3 years ago

Note that the exported HTML retrieved via .getHtml is completely different than what's visible in the editor (all gjs- attributes are missing), so your CSS might not work any more. For exporting, I ended up just grabbing the iframe content instead.

artf commented 3 years ago

@anlumo, unfortunately, grabbing iframe content is not good as each component can have a completely customized view. I mean, it might work for your case but for sure it's not a stable approach.

anlumo commented 3 years ago

Yes, if you have any kind of interactive content, that probably wouldn't work. Fortunately that's not the case for my application.

artf commented 3 years ago

Just a quick update, I've merged the first iteration of the headless option https://github.com/artf/grapesjs/pull/3671 This is not something I'd heavily use, at the moment, but I think this kind of feature might open a lot of possibilities in the future and it also forces to delineate the purely logical part from the presentational one.

You can check the related test case here which illustrates also some of the examples of usage.

I guess the final goal would be to give the possibility to load, edit and store the JSON file by using the same API and, as you can see from the test file, it should already be possible (but I didn't play that much I'd say). So, let's say for example that I need to update all the images of some project file, this is how I'd do it:

const editor = grapesjs.init({ headless: true });
const jsonData = await loadProjectData(projectID); // { pages: [...], styles: [...], ... }
editor.loadData(jsonData);
const mainPage = editor.Pages.getMain();
const wrapper = mainPage.getMainComponent();
// note: can't use `.find` method, as it requires DOM API
wrapper.findType('image').map(img => img.set('src', 'placeholder.jpg'));
const newData = editor.storeData();
await storeProjectData(projectID, newData);

@AdamTorma In case of your initial question, you would do something like this

const editor = grapesjs.init({ headless: true });
const components = editor.addComponents({ type: '...' }); // Component Definition
const html = components.map(cmp => cmp.toHTML()).join('');

At the moment is not possible to use all those methods which would require the use of some DOM API (without using something like jsdom). So, doing this editor.addComponents('<div>....'), which triggers the native parser, would not work. Also, some Component-related methods like find() and closest() require the component to be rendered.

I'll move this issue in a Discussione so for any further update related to the subject, can be discussed there.