Closed mingfang closed 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?
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.
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
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.
I was able to combine Podium layout with both EJS and Nunjucks as Express view engines. The layout using EJS looks like this
and the layout for Nunjucks looks like this
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?