-
-
Save crisrojas/9f3b4c0b0dd4f403c93258b91c37322d to your computer and use it in GitHub Desktop.
Mini templates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
<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