DubStash is a fast, lightweight and simple semantic template engine for HTML.
- Use it in a client-side browser or on a back-end Node.js server.
- Compile a template once, then use it repeatedly with different data.
- Optionally, precompile templates to Javascript at build time for even faster loading.
- Weight: < 6.5 KB
<script src="dubstash.min.js"></script>
Details...- {{propertyName}} writes out HTML-escaped value; {{{propertyName}}} writes out value without HTML-escaping it.
- {{if propertyName}} output this text {{else}} output this other text {{end if}}
- {{foreach collectionPropertyName}} ... {{end foreach}}
- Global templates can be used by other templates as building blocks.
- Recursive evaluation: {{propertyName /r}} or even {{if propertyName /r}}
- Precompilation of templates to Javascript
- Define a template that contains
{{placeholders}}
for values that will be substituted at runtime. - Generate a rendering function by compiling the template.
- Call the rendering function with an object that contains the data to subsitute for the
{{placeholders}}
. - The text that replaces
{{placeholders}}
is escaped for safe use in HTML, unless you use a{{{triple-stash}}}
(not shown).
// Define the template. You normally won't do this using Javascript. Instead you can use the new
// HTML5 <template> tag, or put your templates inside <script type="text/dubstash">...</script>,
// and assign the contents to a Javascript variable using the script tag's innerHTML property.
var template = '<ul><li>My name is {{name}}.</li>' +
'<li>I live in {{address.city}}.</li>' +
'<li>My favourite cartoon is {{cartoon}}.</li></ul>';
// Generate a rendering function that can be used multiple times with different data.
var render = DubStash.compile(template);
// Define some example data objects.
var person1 = {
name: 'John Smith',
address: {
city: 'Cardiff'
},
cartoon: 'Calvin & Hobbes'
};
var person2 = {
name: 'Fred Bloggs',
address: {
city: 'Swansea'
},
cartoon: 'Tom & Jerry'
};
// Render the template with different data.
var output1 = render(person1);
var output2 = render(person2);
The value of output1
is:
<ul>
<li>My name is John Smith.</li>
<li>I live in Cardiff.</li>
<li>My favourite cartoon is Calvin & Hobbes.</li>
</ul>
<!-- Linebreaks and indents added for clarity -->
The value of output2
is:
<ul>
<li>My name is Fred Bloggs.</li>
<li>I live in Swansea.</li>
<li>My favourite cartoon is Tom & Jerry.</li>
</ul>
<!-- Linebreaks and indents added for clarity -->
- You can loop over values that are collections of objects, repeating a snippet of template for each one.
- Inside the loop, the data object is the current member of the collection being iterated. In the
example below
{{name}}
is used twice, but the second instance refers to a different person.- Inside a loop, you can still access the parent object by prefixing the property name with
../
or../../
etc.
- Inside a loop, you can still access the parent object by prefixing the property name with
- Not only arrays can be iterated. Objects can be iterated, as well as anything that has a
forEach
method.
var template = '<p>These are the people that {{name}} invited:</p>' +
'<ul>{{foreach invitees}}<li>{{name}} (invited by {{../../name}})</li>{{end foreach}}</ul>';
var render = DubStash.compile(template);
var person = {
name: 'John Smith',
invitees: [
{name: 'Fred Bloggs'},
{name: 'Jack Jackson'},
{name: 'Mary Black'}
]
};
var output = render(person);
The value of output
is:
<p>These are the people that John Smith invited:</p>
<ul>
<li>Fred Bloggs (invited by John Smith)</li>
<li>Jack Jackson (invited by John Smith)</li>
<li>Mary Black (invited by John Smith)</li>
</ul>
<!-- Linebreaks and indents added for clarity -->
- Use
{{if propertyName}} ... {{else}} ... {{endif}}
for including text conditionally. - An empty array or object will evaluate to
false
, so you can easily test whether a collection has items in it.
var template = '<p>Dear {{if isMale}}Sir{{else}}Madam{{endif}},</p>' +
'{{if itemsOrdered}}'
'<p>These are the items you ordered:</p>' +
'<ul>' +
'{{foreach itemsOrdered}}<li>{{name}}</li>{{end foreach}}' +
'</ul>' +
'{{else}}' +
'<p>You have not ordered anything recently.</p>' +
'{{end if}}';
var render = DubStash.compile(template);
var recipient = {
gender: 'f',
isMale: function(){
return recipient.gender === 'm';
},
itemsOrdered: [
{name: 'Grand Piano'},
{name: 'Violin'},
{name: 'Flute'}
]
};
var output = render(recipient);
The value of output
is:
<p>Dear Madam,</p>
<p>These are the items you ordered:</p>
<ul>
<li>Grand Piano</li>
<li>Violin</li>
<li>Flute</li>
</ul>
<!-- Linebreaks and indents added for clarity -->
- Different templates can make use of common building blocks known as global templates. This provides similar functionality to 'partials' and 'helpers' used in other templating engines.
- Using a global template will not double-escape your HTML.
// 'bestName' is a template that writes out the best name to use for greeting a person:
DubStash.registerGlobalTemplate('bestName',
'{{if nickName}}{{nickName}}{{else}}{{firstName}}{{end if}}');
// Define the text of a generic letter.
var renderLetter = DubStash.compile('<p>Dear {{bestName}},</p>');
var person1 = {
firstName: 'William',
lastName: 'Smith',
nickName: 'Bill'
};
var person2 = {
firstName: 'Frederick',
lastName: 'Bloggs'
};
var output1 = renderLetter(person1);
var output2 = renderLetter(person2);
The value of output1
is:
<p>Dear Bill,</p>
The value of output2
is:
<p>Dear Frederick,</p>
- The global templates example uses recursion: the value of the {{bestName}} placeholder is itself a template that is then evaluated.
- If you want to treat the value of an expression as a template itself, and evaluate it, use the /r
flag, e.g.
{{blurb2 /r}}
Note: You do not need the /r flag when using global templates -- it is implied. - Don't worry, recursive evaluation will not double-escape the HTML.
In the global templates example, the greeting template does not handle a case where we don't know a person's first name or nickname. Let's build a template that makes up for that deficiency.
// 'bestName' is a template that writes out the best name to use for greeting a person:
DubStash.registerGlobalTemplate('bestName',
'{{if nickName}}{{nickName}}{{else}}{{firstName}}{{end if}}');
// Define the text of a generic letter. If we don't know a person's name, say 'Sir'.
var renderLetter = DubStash.compile(
'<p>Dear {{if bestName}}{{bestName}}{{else}}Sir{{end if}},</p>');
var person3 = {
lastName: 'Heisenberg'
};
var output3 = renderLetter(person3);
The value of output3
is:
<p>Dear Sir,</p>
- You can precompile templates into Javascript functions that can be inserted into your source files.
- Runtime startup will be faster because there will be no need for a
DubStash.compile()
step. - You still need to include a link to the DubStash script in your HTML, because precompiled functions still depend on it.
- Global templates that have already been registered are all compiled together as a single block.
// Typically you would do this in Node, where you can save the precompiled functions without having
// to manually copy/paste them into a .js source file.
var DubStash = require('./dubstash.js');
// 'bestName' is a template that writes out the best name to use for greeting a person:
DubStash.registerGlobalTemplate('bestName',
'{{if nickName}}{{nickName}}{{else}}{{firstName}}{{end if}}');
var globalsSource = DubStash.precompileGlobalTemplates();
var template = 'My name is {{bestName}}.';
var rendererSource = DubStash.precompile(template);
// Build the output which should be saved as a JS file.
var output = globalsSource + '\n';
output += 'var render = ' + rendererSource;
...
The resulting output will look like this:
DubStash.G('bestName', function( ... ){ ... });
var render = function( ... ){ ... }
Your HTML file needs to use the DubStash script. Use the distributable version dubstash.min.js.
<html>
<head>
...
<script src="dubstash.min.js"></script>
...
</head>
<body>
...
</body>
</html>
DubStash is compiled and minified using Google Closure Compiler. The following command line does it:
npm run build
npm test