podium-lib / issues

All podium issues, bugs, questions etc goes here. Documentation is to be found at https://podium-lib.io
1 stars 0 forks source link

Question: What do you think about combining Podium with other templating engines for layout? #58

Closed mingfang closed 2 years ago

mingfang commented 2 years ago

I was able to combine Podium layout with both EJS and Nunjucks as Express view engines. The layout using EJS looks like this

<%- await podlets.header.fetch(res.locals.podium, {query: req.query}) %>

and the layout for Nunjucks looks like this

{{ podlets.header.fetch(res.locals.podium, {query: req.query}) | await }}

Note for Nunjunk I had to create a custom filter called await because the native await syntax is not supported.

I think combining templating engines with Podium is really powerful. What do you think?

digitalsadhu commented 2 years ago

Hey @mingfang !

Are you thinking we should add some kind of support into Podium for this?

I like the idea of calling fetch in templates. You would need to gather up fetches and do parallel fetching in the view engine. I've done some experiments using tagged template literals that worked quite nicely for this. Eg.

const templateResult = await tmpl`<div>
    ${podlets.header.fetch(res.locals.podium, {query: req.query})}
    ${podlets.footer.fetch(res.locals.podium, {query: req.query})}
</div>`

Using this approach, it's possible to fetch all podlets in parallel which is nice. Perhaps this can also be done in template engines too?

mingfang commented 2 years ago

I'm not sure we need Podium to support any template engines natively, but just sharing some ideas. I just verified my approach with Nunjucks does not do parallel fetching and that's a huge problem for me. Looking at TailorX https://github.com/StyleT/tailorx, they seem to have a custom template engine that can fetch in parallel. Also Dust and Marko appear to support streaming but I have not tried them yet.

mingfang commented 2 years ago

I think I'm going with a new direction of trying to stream the podlet content by lazy loading them from the browser. It seems to work nicely using htmx https://htmx.org

mingfang commented 2 years ago

I finally managed to integrate Nunjucks with Podium on the Layout server. Here is the code

const env = nunjucks.configure('templates')

const nameToPodlet = new Map(Array.from(layout.client).map(i => [i.name, i]));
env.addFilter('fetch', (name, incoming, args = {}) => {
  incoming.fetches.push({podlet: nameToPodlet.get(name), args: args})
  // placeholder to be replaced by podlet content
  return 'PODLET' + (incoming.fetches.length - 1)
})

app.get(`${layout.pathname()}/:template?`, async (req, res) => {
  const incoming = res.locals.podium
  // hack to hold fetch requests from njk file
  incoming['fetches'] = []
  const template = req.params['template'] || 'index'
  env.render(`${template}.njk`, {req, res, incoming}, async (err, fragment)=>{
    // fetch all
    const responses = await Promise.all(incoming.fetches.map(each => each.podlet.fetch(incoming, each.args)))
    // add assets
    incoming.podlets = responses
    //replace PODLET# added by filter with the responses
    let final = fragment
    for (const [i, each] of responses.entries()) {
      final = final.replace('PODLET' + i, each.content)
    }
    res.podiumSend(final)
  })
})

Then I can create Nunjucks templates like this

<h1>1</h1>
{{ 'quil' | fetch(incoming) }}
<h1>2</h1>
{{ 'myPodlet' | fetch(incoming, {query: req.query}) }}

Now I can create many Nunjucks layouts without having to touch the express code at all.