Last active
February 6, 2020 15:35
-
-
Save EmielZuurbier/c297d5583aae9f29662d0951af375f26 to your computer and use it in GitHub Desktop.
Wrapper for creating a reusable template string based on a callback. Provides ability to render and re-render the template, and insert a clone of the template anywhere in the document.
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
/** | |
* Wrapper for creating a reusable template string based on a callback given | |
* by the user. The template string will be wrapped in a <template> element to | |
* clone the instance of the rendered template. | |
*/ | |
class Template { | |
/** | |
* Rendered string of template. | |
* | |
* @private | |
*/ | |
#rendered = null; | |
/** | |
* Store the callback to render the data with. | |
* | |
* @private | |
*/ | |
#renderCallback = null; | |
/** | |
* Give a function which returns a template string to render your data. | |
* | |
* @param {Function} renderCallback Function which returns a template string. | |
*/ | |
constructor(renderCallback) { | |
this.renderCallback = renderCallback; | |
} | |
/** | |
* Gets the rendered property | |
* | |
* @property | |
*/ | |
get rendered() { | |
return this.#rendered; | |
} | |
/** | |
* Gets and sets the renderCallback property. | |
* | |
* @property | |
*/ | |
get renderCallback() { | |
return this.#renderCallback; | |
} | |
/** | |
* @property | |
*/ | |
set renderCallback(value) { | |
if ('function' === typeof value) { | |
this.#renderCallback = value; | |
} | |
} | |
/** | |
* Render the string by injecting any amount of arguments | |
* into the renderCallback given in the constructor. | |
* | |
* @param {...any} args | |
* @returns {string} | |
*/ | |
render(...data) { | |
if (this.renderCallback === null) throw new Error('No renderCallback has been set'); | |
try { | |
const template = document.createElement('template'); | |
const rendered = this.renderCallback(...data); | |
template.innerHTML = rendered; | |
this.#rendered = template; | |
return template; | |
} catch(error) { | |
console.error('renderCallback error. Rendered value is reset to null.', error); | |
} | |
this.#rendered = null; | |
return null; | |
} | |
/** | |
* Returns a cloned DocumentFragment from the rendered template. | |
* | |
* @returns {DocumentFragment} | |
*/ | |
clone() { | |
const rendered = this.rendered; | |
if (rendered === null) throw new Error('There is no rendered template to clone. Render the template first before cloning.'); | |
const template = rendered.content.cloneNode(true); | |
return template; | |
} | |
/** | |
* Uses Node.appendChild to insert a clone of | |
* the template contents into the document. | |
* | |
* @param {HTMLElement} target | |
* @returns {boolean} | |
*/ | |
appendTo(target) { | |
try { | |
const template = this.clone(); | |
target.appendChild(template); | |
} catch(error) { | |
console.error(error); | |
} | |
} | |
} | |
/** | |
* @example | |
* Create a new template based on the given callback. | |
*/ | |
const card = new Template(({ title, body, href }) => ` | |
<article class="card"> | |
<header class="card__header"> | |
<h1>${title}</h1> | |
</header> | |
<div class="card__body"> | |
<p>${body}</p> | |
</div> | |
<div class="card__footer"> | |
<a href="${href}">More</a> | |
</div> | |
</article> | |
`); | |
/** | |
* @example | |
* Render the card with the given data. | |
*/ | |
card.render({ | |
title: 'Hello World', | |
body: 'Now this is podracing', | |
href: 'http://areallycoolwebsite.com' | |
}); | |
/** | |
* @example | |
* Insert the card into the body. | |
*/ | |
card.appendTo(document.body); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment