Skip to content

Instantly share code, notes, and snippets.

@crisrojas
Created March 25, 2024 14:37
Show Gist options
  • Save crisrojas/9f3b4c0b0dd4f403c93258b91c37322d to your computer and use it in GitHub Desktop.
Save crisrojas/9f3b4c0b0dd4f403c93258b91c37322d to your computer and use it in GitHub Desktop.
Mini templates
/*
<each> tag for async fetch of a collection.
Just a toy playground for demo purposes of what I think would be a cool delcartive api to have,
so it misses a lot of important things (recursivity, for example)
Usage:
<each name="recipe" src="/data/recipes.json">
<view
src="/views/card.html"
title={{recipe.title}}
image={{recipe.image}}
price={{recipe.price}}
description={{recipe.description}}
/>
</each>
*/
// each-block-parser.js
document.addEventListener("DOMContentLoaded", async function () {
await parseEachBlocks();
});
async function parseEachBlocks() {
const eachBlocks = document.getElementsByTagName("each")
Array.from(eachBlocks).forEach(async eachBlock => {
const itemName = eachBlock.getAttribute("name");
const src = eachBlock.getAttribute("src")
const content = eachBlock.innerHTML
try {
const r = await fetch(src)
const data = await r.json()
let renderedHTML = await renderBlock(content, itemName, data)
renderedHTML = await parseViewImports(renderedHTML);
eachBlock.outerHTML = renderedHTML
} catch (error) {
console.log("Error fetching JSON:", error)
}
})
}
async function renderBlock(content, itemName, data) {
let renderedHTML = ""
const regex = new RegExp(`{{${itemName}\.([^{}]+)}}`, "g");
data.forEach(item => {
let tempHTML = content;
let match;
while ((match = regex.exec(content)) !== null) {
const property = match[1];
tempHTML = tempHTML.replace(match[0], item[property]);
}
renderedHTML += tempHTML;
});
return renderedHTML
}
async function parseViewImports(views) {
const parser = new DOMParser();
const doc = parser.parseFromString(views, 'text/html');
const viewNodes = doc.body.getElementsByTagName("view");
// Use Promise.all to handle multiple fetch requests concurrently
const promises = Array.from(viewNodes).map(async view => {
const src = view.getAttribute("src");
const response = await fetch(src);
const content = await response.text();
const attributes = getAttributes(view);
if (view.attributes.length == 1 && src != null) {
return "@todo"
} else {
return replaceAttributes(content, attributes);
}
});
// Wait for all promises to resolve and replace view nodes with parsed content
const parsedContents = await Promise.all(promises);
for (let i = 0; i < viewNodes.length; i++) {
views = views.replace(viewNodes[i].outerHTML, parsedContents[i]);
}
return views;
}
function getAttributes(element) {
const attributes = {};
Array.from(element.attributes).forEach(attr => {
if (attr.name !== 'src') {
attributes[attr.name] = attr.value;
}
});
return attributes;
}
function replaceAttributes(html, attributes) {
let parsedHTML = html;
Object.keys(attributes).forEach(attribute => {
const regex = new RegExp(`{{${attribute}}}`, 'g');
parsedHTML = parsedHTML.replace(regex, attributes[attribute]);
});
return parsedHTML;
}
function nodify(stringed) {
const parser = new DOMParser();
const doc = parser.parseFromString(stringed, 'text/html');
return doc.body.firstChild;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment