bite-templates
Version:
Tiny, Dependecy-Free JavaScript Templating
347 lines (293 loc) • 12.3 kB
HTML
<html>
<head>
<meta charset="utf-8">
<title>Bite.js: JavaScript Templating</title>
<link rel="stylesheet" type="text/css" href="demo/colorcoded.css">
<link rel="stylesheet" type="text/css" href="demo/demo.css">
</head>
<body>
<header class="page-header">
<h1 class="page-title">Bite.js</h1>
<h2 class="page-subtitle">Tiny, Dependecy-Free JavaScript Templating</h2>
<a class="github-button button" href="https://github.com/Sinova/Bite.js">View on GitHub</a>
</header>
<div class="page-content">
<section>
<h1 class="section-title">Overview</h1>
<div class="section-content">
Bite.js is an ultra light-weight, full-featured templating engine designed for size and performance.
It compiles templates into standalone functions in vanilla JavaScript that can be used to render
those templates with a given set of data.
</div>
</section>
<section>
<h1 class="section-title">Features</h1>
<div class="section-content">
<ul class="feature-list">
<li>Interpolation</li>
<li>Conditionals</li>
<li>Iteration</li>
<li>Parent/Child Scoping</li>
<li>Arbitrary Expressions</li>
<li>Precompilation or Run-Time Compilation</li>
<li>Partials</li>
</ul>
</div>
</section>
<section>
<h1 class="section-title">Installation</h1>
<div class="section-content">
<code class="code-block">
npm install bite-templates
</code>
</div>
</section>
<section id="usage">
<h1 class="section-title">Usage</h1>
<div class="section-content">
<p>Here is a simple example of how to compile and render a template.</p>
<code class="code-block foo">const render = Bite('Hello, {{$.name}}!');
const result = render({name : 'Sam'});
console.log(result); // Hello, Sam!</code>
<p>
By default, rendering a template with data returns a string.
Passing <i>true</i> as the second parameter to the render function will return a <a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment" target="_blank">DocumentFragment</a>.
This allows you to perform more complicated work on the DOM nodes such as adding event listeners.
</p>
<code class="code-block">const render = Bite('Hello, {{$.name}}!');
const result = render({name : 'Sam'}, true);
console.log(result); // [object DocumentFragment]
document.body.appendChild(result);</code>
<p>Now that we know how to compile and render templates, let's look at a more complicated example:</p>
<code class="code-block">const template = `
{{#if ($.num1 + $.num2) % 2}}
The sum is <b>odd</b>.
{{#else}}
The sum is <b>even</b>.
{{/if}}
`;
const render = Bite(template);
const result = render({num1 : 10, num2 : 4});
console.log(result); // The sum is <b>even</b>.</code>
<p>Here we demonstrate that arbitrary expressions can be evaluated within templates. Additionally, these expressions can be used within conditionals to determine what gets displayed.</p>
<p>Bite.js offers quite a few powerful features that allow you to control what gets rendered. Check out the <a href="#template-api">Template API</a> for a full list of features. You can also view and edit the <a href="#demo">live demo</a>.</p>
</div>
</section>
<section>
<h1 class="section-title">Scopes</h1>
<div class="section-content">
<p>
Within a template, the current context, or <i>scope</i>, is always accessible via the <code>$</code> variable.
For instance, if your data looks like <code>{name : 'Sam'}</code>, your template can access that variable
with <code>$.name</code> and can perform actions on it such as <a href="#interpolation">interpolation</a>.
</p>
<p>
Within <a href="#with"><code>{{#with <expression>}}</code></a> blocks, the scope is changed to the result of the expression passed to it.
Similarly, within <a href="#forEach"><code>{{#forEach <array>}}</code></a> blocks, the scope is changed to the current item in the array.
In these cases, you can access the parent scope with <code>$$</code>.
For example, to output a property from the parent scope you use <code>{{$$.example}}</code>.
You can climb further up the parent scope chain via <code>$$.$$</code>, <code>$$.$$.$$</code>, and so on.
For example: <code>{{$$.$$.example}}</code>
</p>
</div>
</section>
<section id="partials">
<h1 class="section-title">Partials</h1>
<div class="section-content">
<p>
Partials are templates included inside of other templates.
This functionality is implemented inherently via <a href="#html-interpolation">HTML Interpolation</a>.
HTML Interpolation supports both raw HTML and DOM nodes, so you can use both the string and DocumentFragment forms of the partial's render function.
</p>
<p>
Partials can be more easily explained via example:
</p>
<code class="code-block">const render = Bite('Hello, {{$.name}}! {{% $.reverse_partial({first_name : $.name})}}');
const partial = Bite('Your name reversed is: {{$.first_name.reverse()}}!');
const result = render({
name : 'Sam',
reverse_partial : partial,
});
console.log(result); // Hello, Sam! Your name reversed is: maS!</code>
</div>
</section>
<section>
<h1 class="section-title">Pre-Compilation</h1>
<div class="section-content">
<p>
Templates can be pre-compiled into functions on the server within your Node.js application.
Calling <code>toString()</code> on a compiled template function will return a string which can then be saved however you wish (file, database, etc).
These strings can be sent straight to the client as either a standalone JavaScript file or as part of other source files depending on your build workflow.
</p>
<p>
If you're using Webpack, you can use the <a href="https://github.com/Sinova/bite-templates-loader" target="_blank">bite-templates-loader</a> package to automatically precompile your templates before sending them to the client.
</p>
</div>
</section>
<section id="template-api">
<h1 class="section-title">Template API</h1>
<div class="section-content">
<table class="api-table">
<tr id="interpolation">
<th>
<h3>Interpolation</h3>
<code>{{<expression>}}</code>
</th>
<td>
<b>Interpolation</b> allows you to inject arbitrary values and expressions into your template.
The most common use-case is outputting a property such as a name or date, e.g. <code>{{$.name}}</code>.
However, any valid JavaScript expression is allowed, such as <code>{{1 + 2 + 3}}</code>, <code>{{'Mr. ' + $.last_name}}</code>, or <code>{{$.name.toUpperCase()}}</code>.
<blockquote>
<b>Note:</b> Interpolated values are HTML-escaped to avoid injection attacks.
If you need to inject HTML without escaping, see <a href="#html-interpolation">HTML Interpolation</a>.
</blockquote>
</td>
</tr>
<tr id="html-interpolation">
<th>
<h3>HTML Interpolation</h3>
<code>{{% <expression>}}</code>
</th>
<td>
<b>HTML Interpolation</b> allows you to inject arbitrary values and expressions into your template <i>without</i> HTML-escaping them.
This can also be used to inject DOM nodes instead of HTML strings (see the <a href="#partials">partials section</a> for more information).
<blockquote>
<b>Caution:</b> HTML Interpolating untrusted strings and DOM nodes can lead to potential injection attacks. Use wisely!
</blockquote>
</td>
</tr>
<tr>
<th>
<h3>If</h3>
<code>{{#if <expression>}}
...
{{#elseif <expression>}}
...
{{#else}}
...
{{/if}}</code>
</th>
<td>
The <b>if</b> block allows you to control the flow of your code by testing certain conditions.
Within the block, <code>{{#elseif <expression>}}</code> and <code>{{#else}}</code> blocks can optionally be used.
Any valid JavaScript expression can be used with <code>#if</code> and <code>#elseif</code>.
</td>
</tr>
<tr id="repeat">
<th>
<h3>Repeat</h3>
<code>{{#repeat <number>}}
...
{{/repeat}}</code>
</th>
<td>
The <b>repeat</b> block allows you to repeat a subsection of the template a given number of times.
Within the block, <code>i</code> becomes the current iteration counter.
<code>#repeat</code> behaves like a standard <code>for</code> loop, starting with 0 and counting up to, but not including, the target number.
</td>
</tr>
<tr id="forEach">
<th>
<h3>ForEach</h3>
<code>{{#forEach <array>}}
...
{{/forEach}}</code>
</th>
<td>
The <b>forEach</b> block allows you to loop over an array of values and output a subsection of the template.
The result of the expression passed to <code>#forEach</code> must be an array.
Within the block, <code>i</code> becomes the current index in the array, <code>$</code> becomes the current value, and <code>$$</code> becomes the parent scope.
You can climb further up the parent scope chain via <code>$$.$$</code>, <code>$$.$$.$$</code>, and so on.
</td>
</tr>
<tr id="with">
<th>
<h3>With</h3>
<code>{{#with <expression>}}
...
{{/with}}</code>
</th>
<td>
The <b>with</b> block allows you to change the current scope within the template.
<code>$</code> becomes the value of the expression passed to the block, and <code>$$</code> becomes the parent scope.
You can climb further up the parent scope chain via <code>$$.$$</code>, <code>$$.$$.$$</code>, and so on.
</td>
</tr>
</table>
</div>
</section>
<section id="demo">
<h1 class="section-title">Demo</h1>
<div class="section-content">
<p>Here is a live, editable demo showing use of all the features Bite.js has to offer. Feel free to experiment.</p>
<div class="demo-columns">
<div class="demo-section">
<div class="demo-title">Template</div>
<textarea class="code-block" id="demo-template" spellcheck="false">
<h1>{{$.name}}</h1>
<h2>Profession</h2>
{{#with $.profession}}
<div>{{$.name}} for {{$.years}} years</div>
<div>{{% $$.star_partial({max : $.rating})}}</div>
{{/with}}
{{#with $.interests}}
<h2>Interests ({{$.items.length}})</h2>
<ul>
{{#forEach $.items}}
<li>{{$$.$$.name}} {{$$.verb}} {{$}}</li>
{{/forEach}}
</ul>
{{/with}}
</textarea>
</div>
<div class="demo-section demo-js-container">
<div class="demo-title">JavaScript</div>
<textarea class="code-block" id="demo-js" spellcheck="false">
var data = {
name : 'Sam',
profession : {
name : 'Programmer',
years : '6',
rating : 4,
},
interests : {
verb : 'likes',
items : [
'Chess',
'Boxing',
'Bite.js',
],
},
// Inline partial just for demoing purposes
star_partial : Bite(`
{{#repeat 5}}
{{#if i < $.max}}
★
{{#else}}
☆
{{/if}}
{{/repeat}}
`),
};
var render = Bite(template);
output.innerHTML = render(data);
</textarea>
</div>
</div>
<div class="demo-section">
<div class="demo-title">Output</div>
<div id="demo-output"></div>
</div>
<div class="demo-section">
<div class="demo-title">Compiled Function</div>
<code id="demo-compiled"></code>
</div>
</div>
</section>
</div>
<script src="bite.js"></script>
<script src="demo/colorcoded.js"></script>
<script src="demo/demo.js"></script>
</body>
</html>