JSON to html, loads slowly using formatters. How to do this properly?

I have a page that needs to load a lot of dynamic content.
This content is stored as a json object (generated by https://editorjs.io/ , example object:

{
	"body": {
		"time": 1655912890286,
		"blocks": [
			{
				"id": "6el1Ox2NGo",
				"data": {
					"text": "Heading"
				},
				"type": "paragraph"
			},
			{
				"id": "LyOvywDEYC",
				"data": {
					"text": "Paragraph text goes here<br>"
				},
				"type": "paragraph"
			},
			{
				"id": "sokGcDrI-7",
				"data": {
					"file": {
						"url": "https://dl.smartclasses.app/school/1/reading-page/9/content/parsifal5_2.jpg"
					},
					"caption": "man at facebook",
					"stretched": false,
					"withBorder": false,
					"withBackground": false
				},
				"type": "image"
			},
			{
				"id": "OOxO_OLNJx",
				"data": {
					"text": "There is a lot of content here. This is a paragraph that I am just typing now."
				},
				"type": "paragraph"
			},
			{
				"id": "JtyXtSS8xK",
				"data": {
					"text": "And this is another paragraph."
				},
				"type": "paragraph"
			},
			{
				"id": "D5OMTNppMj",
				"data": {
					"text": "This is another heading",
					"level": 2
				},
				"type": "header"
			},
			{
				"id": "nR8g6Bhqj2",
				"data": {
					"text": "‍Smaller heading ",
					"level": 3
				},
				"type": "header"
			},
			{
				"id": "RTu1j7SrkY",
				"data": {
					"text": "more text<br>"
				},
				"type": "paragraph"
			},
			{
				"id": "4QLuFaz6Qc",
				"data": {
					"link": "smartclasses.app",
					"text": "Lovely CTA"
				},
				"type": "AnyButton"
			}
		],
		"version": "2.24.3"
	}
}

I load it on the page, but it shows up very slowly. Probably due to the many repeating appconnect formatters.

This is how I’m parsing it now to show on the page:
(the input of the repeat is the object shown above)

<div dmx-repeat:repeat1="reading_page_get.data.query_reading_page_info.body.blocks">

                            <div id="conditionalParagraph" is="dmx-if" dmx-bind:condition="type=='paragraph'">
                                <p>{{data.text}}</p>
                            </div>
                            <div id="conditionalH2" is="dmx-if" dmx-bind:condition="type=='header'&&data.level==2">
                                <h2 class="rp-h2">{{data.text}}</h2>
                            </div>
                            <div id="conditionalH3" is="dmx-if" dmx-bind:condition="type=='header'&&data.level==3">
                                <h3 class="rp-h3">{{data.text}}</h3>
                            </div>
                            <div id="conditionalH4" is="dmx-if" dmx-bind:condition="type=='header'&&data.level==4">
                                <h4 class="rp-h4">{{data.text}}</h4>
                            </div>
                            <div id="conditionalImage" is="dmx-if" dmx-bind:condition="type=='image'">
                                <figure class="figure">
                                    <img dmx-bind:src="{{data.file.url}}" class="figure-img img-fluid rounded" dmx-bind:alt="{{data.caption}}">
                                    <figcaption class="figure-caption" dmx-show="data.caption">{{data.caption}}</figcaption>
                                </figure>
                            </div>
                            <div id="conditionalButton" is="dmx-if" dmx-bind:condition="type=='AnyButton'">
                                <div class="d-grid col-6 mx-auto">
                                    <a class="btn btn-primary" dmx-bind:href="{{data.link}}">{{data.text}}</a>
                                </div>
                            </div>
                        </div>

This is loading a lot of elements in the DOM as well

There must be a better way to do what I’m doing here. Please tell me!

PS to clarify: for each object I want to check what type it is, then based on that type I want to show certain HTML. So let’s say type=="image" then I want to load <img... with the other values in that object.

Do you need all these to be conditionals - they will all need to be rendered and you have 3 just for headers for each element? Could you just show/hide some? At the moment you are creating 6 divs in the dom for every entry in the JSON.

For example you could use one conditional for headers and show/hide the level depending on the level value

For others you could have something like:

<p dmx-show="type=='paragraph'" dmx-text="type=='paragraph' ? data.text : ''"></p>

It will hide and have no text inside the tags if the element is not a paragraph.

Ideally, I think there’s a server-side solution using EJS (assuming NodeJS). I’m not overly familiar with it but looking at the docs, it would be something like this on your page - just use the server-side data linked to your SC API file from the app node in App Structure:

<% (_('query_reading_page_info.body.blocks')).forEach(function(item){ %>
    <%if (item.type == 'paragraph') { %>
        <p><%= item.data.text %></p>
    <% } %>
    <%if (item.type == 'header') { %>
        <h<%= item.data.level %>><%= item.data.text %></h<%= item.data.level %>>
    <% } %>
<% }) %>

the heading tag will also complete based on the level property’s value

By making it server-side, only the relevant components will be rendered so you don’t end up with stacks of empty elements/conditionals

3 Likes

Thanks Ben, will try this first thing in the morning! Appreciate you writing out some example code!

Hopefully it’ll pseed it up, as it’s already taking a few seconds.

And yes I’ve worked a bit with EJS before, but I’m curious why you’re saying that is the ideal solution?
Isn’t that a similar way of doing this, just more server intensive?

EDIT:

By making it server-side, only the relevant components will be rendered so you don’t end up with stacks of empty elements/conditionals

Just realised you wrote this. So that’s why it’s preferred?

1 Like

Say you have 10 items on the page, You would have 60 conditional divs plus the tags that render the content you want. Way more than you need. Plus the browser is doing all the work to evaluate what conditions should show or not for 60 areas.

1 Like

We have created a server-side library action, which returns HTML from data stored in JSON, as defined.
And we bind that HTML directly on the page using dmx-html.
It works great and really fast, only problem is that this HTML cannot contain anything related to Wappler’s AppConnect/DMX.

Using @bpj’s server side binding condition, I think that limitation can be overcome as well.

But for your use case of editor js, that is not really needed. So if you find the server side setup difficult, you can try creating HTML on Server Action and return that instead of the JSON.

1 Like