block-inheritance-templating v1.0.1
block-inheritance-templating
block based template rendering engine
Features
- block based template inheritance (blocks can be overwritten or reused)
- translation hooks (integrates with nearly all translation apis)
- caching hooks (integrates with nearly all caching apis)
- livecycle hooks (easy to add new features, e.g. css collecting)
- extendable (all kinds of objects and functions can be passed into the template)
Advantages
- small (~1.7kb minified)
- fast
- dependency free
- all features are documented and covered by tests
- pure javascript templates (no extra parsing overhead)
- dual use: frontend (browser) and backend (node)
- can be used with javascript template literals
- allows a logic-less template style or full-fledged template scripting
install
npm install block-inheritance-templatinghello world
(async () => {
const { render } = await import('block-inheritance-templating')
render({
parent: null,
block: {
main: () => "hello world"
}
}).then(content => console.log(content))
})()the template
Every object that has a parent and a block property is a template.
The parent property is null or points to another template. As the parent can
have a parent template of its own, every template is the beginning of a template
(inheritance) chain. If a lookup in a template fails the lookup is repeated in
the parent, then in the parents parent and so on. Therefor the template chain
should not create a circle.
The block property is an object of template blocks...
const someTemplate = {
parent: null,
block: {
main: (vars, local, fnc) => `...`,
someBlock: (vars, local, fnc) => `...`,
anotherBlock: (vars, local, fnc) => `...`,
},
}or a function returning template blocks.
const someTemplate = {
parent: null,
block: name => {
switch (name) {
case 'main': return (vars, local, fnc) => `...`
case 'someBlock': return (vars, local, fnc) => `...`
case `anotherBlock`: return (vars, local, fnc) => `...`
default: return null
}
},
}a template block
A template block is an async function rooted in the block property of the
template object. Its name is the property name. There are three arguments to
this function vars, local and fnc supplied by the rendering engine.
A template block has to return a string value.
the vars argument
The vars argument is a function by which all variables can be accessed that
have been passed to the render call or are rooted in the vars property of
the template chain.
const variable = vars('someVariable')the fnc argument
The fnc object holds user-land functions passed to the render call and the
three (async) rendering functions block, ìterate and render.
- fnc.block (blockName, localVars)
- fnc.iterate (blockName, data, separator='')
- fnc.render (tpl, variables, lng, fnc={}, entrypoint='main')The block function renders a block. It has to be a block of the same template
chain identified by the blocks name. As second argument local variables can be
passed. If the same variable exists in the current vars function, it will be
overwritten by the passed variable.
const renderedBlock = await fnc.block('someBlock', { someVar: 42, anotherVar: 'Hello World' })The iterate function iterates block function calls over a data array. The
result will be joined via the passed separator.
const renderedBlocks = await fnc.iterate('someBlock', [/*...*/], '<hr/>')The render function is the render function. By it one can render other
template chains or the same template chain with other arguments.
const renderedTemplate = await fnc.render(template, vars, lang, fnc, entrypoint)the local argument
The local argument holds some special functionality:
- local.parent()
- local.lang
- local.index
- local.vars(name)
- local.trans(item, category=null)
- local.block(blockName, localVars)
- local.iterate(bockName, data, separator='')local.parent() looks for the same block in the templates inheritance chain and
renders it with the same arguments.
const renderedParentBlock = await local.parent()local.lang holds the current language in which the template is rendered.
const currentLang = local.langlocal.index holds the current iteration index or null.
const loopIndexIsEven = (local.index % 2 === 0)local.vars is like the vars argument but holds the variables explicitly
passed to the last block call only.
const localVariable = local.vars('someLocalVariable')local.trans translates an item in the current language via the special
function trans().
const translatedString = local.trans('hello', 'snippet.words')local.block is the same as fnc.block without access to the global scope. If
the local vars are omitted the current scope is used. Template vars remain
always accessible as fallback.
local.iterate is the same as fnc.interate without access to the global scope.
Template vars remain always accessible as fallback.
special functions
Some functions have a special meaning to the template engine. They can be passed
by the fnc object or be rooted in the fnc property of the template
inheritance chain.
- trans (item, category, lang, blockName, template, vars, local, fnc) => ...
- onError (e, lang, blockName, template, vars, local, fnc) => ...
- getCache ( lang, blockName, template, vars, local, fnc) => ...
- setCache (result, lang, blockName, template, vars, local, fnc) => ...
- preRender ( lang, blockName, template, vars, local, fnc) => ...
- postRender ( lang, blockName, template, vars, local, fnc) => ...
- preCall ( lang, blockName, template, vars, local, fnc) => ...
- postCall ( lang, blockName, template, vars, local, fnc) => ...trans
trans() is the translation function it can be accessed in the template via
local.trans(item, category).
onError
onError() will be called if rendering or one of the other special functions
throws an error. If onError() does not throw an error the result is used
instead of the rendered.
If no onError() function is found the error will be rethrown.
getCache, setCache
getCache() and setCache() can be used to cache rendered blocks.
preRender, postRender
preRender() will be called pre and postRender() post rendering. Both will
not be called if the cache hits.
preCall, postCall
preCall() will be called pre and postCall() post rendering. Both will
be called with or without a cache hit.
how to render a template
const { render } = await import('block-inheritance-templating')or
import { render } from 'block-inheritance-templating'then
const vars = {}
const lang = 'en'
const fnc = {}
const entrypoint = 'main'
const output = await render(template, vars, lang, fnc, entrypoint)Basic Example
const { render } = await import('block-inheritance-templating')
const parentTemplate = {
parent: null,
vars: {
content: '<p>some content</p>',
},
block: {
main: async (vars, local, fnc) => {
return `<!doctype html>
<html lang="${ await local.trans('lang') }">
<head>
<title>${ vars('title') }</title>
${ await fnc.block('head') }
</head>
<body>
${ await fnc.block('nav') }
<h1>${ vars('title') }</h1>
${ await fnc.block('content') }
</body>
</html>`
},
head: async function() {
return `<script>let that, be, empty</script>`
},
},
}
const renderedTemplate = {
parent: parentTemplate,
block: {
nav: async (vars, local, fnc) => {
return `
<nav>${ await fnc.iterate('navItemBlock', vars('navItems')) }
</nav>`
},
navItemBlock: async (vars, local) => {
return `
<a href=${ vars('href') }>(${ local.index }) ${ vars('content') }</a>`
},
content: (vars) => vars('content'),
},
}
const vars = {
title: 'Hello World',
navItems: [
{ href: 'https://hello.com', content: 'hugs to you' },
{ href: 'https://world.com', content: 'global issues' },
],
}
const lang = 'en'
const fnc = {
trans: function(name, category, lang) {
switch (name) {
case 'lang': return lang
default: return name
}
}
}
const output = await render(renderedTemplate, vars, lang, fnc);
// c o n s o l e . l o g (output) gives:
//
// <!doctype html>
// <html lang="en">
// <head>
// <title>Hello World</title>
// <script>let that, be, empty</script>
// </head>
// <body>
//
// <nav>
// <a href=https://hello.com>(0) hugs to you</a>
// <a href=https://world.com>(1) global issues</a>
// </nav>
// <h1>Hello World</h1>
// <p>some content</p>
// </body>
// </html>