Fast and simple asynchronous JavaScript template engine without dependencies and for any environment (:
I wanted to create something simple for what you don't need to spend hours to read the documentation, something that you can start to use in a few minutes. The main idea is using pure JavaScript with pure HTML. You don't need to learn new syntax, just do everything like in JavaScript.
- Embedded JavaScript code
- Browser support
- Complies with Express
- Layouts, partials and sections
- No dependencies and very small size
- TypeScript support
- Easy to use
Using yarn or npm:
$ yarn add atat
$ npm install --save atat
Using yarn or npm:
$ yarn test
$ npm test
import { config, parse, loadAndRender, render, loadAndParse } from 'atat';
var atat = require('atat');
atat
object has the following methods
config
to setup global configuration for all templatesparse
to parse a template, returns a render functionloadAndParse
the same withparse
but allows a path to a template as the first argumentrender
to parse and render a template, returns the result stringloadAndRender
the same withrender
but allows a path to a template as the first argument
import { parse, loadAndRender, render, loadAndParse } from 'atat';
const render = await parse(templateString, options);
container.innerHTML = render(model);
container.innerHTML = await render(templateString, model, options);
const render = await loadAndParse(pathToTemplate, options);
container.innerHTML = render(model);
container.innerHTML = await loadAndRender(pathToTemplate, model, options);
If your environment doesn't support async/await
sytax, use Promise
render(templateString, options).then((err, result) => {
container.innerHTML = result;
});
it
models variable name, default"it"
$
helpers variable name, default"$"
helpers
extra helpersloader
templates provider
import { parse, render, config, DEFAULT_LOADER } from 'atat';
const options = {
it: "it",
$: "$",
helpers: { },
loader: DEFAULT_LOADER
};
// global config will be applied to all templates
config(options);
// also you can pass options to the parse and render methods
const tmpl = await parse(templateString, options);
const html = await render(templateString, { /* model */ }, options);
Loaders allow you to load templates asynchronously. There are two default loaders available right from the library:
DEFAULT_LOADER
- for Node.js, default loader, usesfs
moduleFETCH_LOADER
- for a browser, loads templates throughfetch
method
import { loadAndRender, loadAndParse, config, FETCH_LOADER, DEFAULT_LOADER } from 'atat';
const html = await loadAndRender(
path.resolve(__dirname, './views/main.html'),
{ /* model */ },
{ loader: DEFAULT_LOADER },
);
// in a browser you must specify loader, at least FETCH_LOADER
const html = await loadAndRender(
'http://localhost:3000/views/main.html',
{ /* model */ },
{ loader: FETCH_LOADER },
);
// custom loader
config({
loader: async (path) => {
const template = await loadTemplate(path);
return template;
}
});
Encoded output
<p>@(it.user.firstName)@</p>
<!--
Model: { user: { firstName: 'Charlotte' } }
Output:
<p>Charlotte</p>
-->
Raw html output
<p>@!(it.rawHTML)@</p>
<!--
Model: { rawHTML: '<i>Hello!</i>' }
Output:
<p><i>Hello!</i></p>
-->
Embedded JavaScript code
@{
// Any JavaScript code is acceptable in this block
var firstName = it.user.firstName;
var secondName = it.user.secondName;
}@
<p>@(firstName)@ @(secondName)@</p>
<!--
Model: { user: { firstName: 'Charlotte', secondName: 'Gamboa' } }
Output:
<p>Charlotte Gamboa</p>
-->
@if
@if (it.user != null) {
<p>@(it.user.firstName)@</p>
<p>@(it.user.secondName)@</p>
}@
<!--
Model: { user: { firstName: 'Charlotte', secondName: 'Gamboa' } }
Output:
<p>Charlotte</p>
<p>Smith</p>
-->
@if...else if...else
@if(it.user && it.user.firstName && it.user.secondName){
<p>@(it.user.firstName)@</p>
<p>@(it.user.secondName)@</p>
} else if (it.user && it.user.firstName) {
<p>@(it.user.firstName)@</p>
} else {
<p>User is not defined</p>
}@
<!--
Model: { user: { firstName: 'Charlotte', secondName: '' } }
Output:
<p>Charlotte</p>
-->
@for
<ul>
@for(var i = 0, l = it.users.length; i < l; i++){
<li>@(it.users[i].firstName)@ @(it.users[i].secondName)@</li>
}@
</ul>
<!--
Model: { users: [{ firstName: 'Charlotte', secondName: 'Gamboa' }] }
Output:
<ul>
<li>Charlotte Gamboa</li>
</ul>
-->
@while
<ul>
@{ var i = 0, j = 5; }@
@while (i < j) {
<li>@(i++)@</li>
}@
</ul>
<!--
Output:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
-->
@<name>(<args...>)@
name
the valid nameargs...
whatever you want
const options = {
$: '$',
helpers: {
l10n: (lang, key) => resources[lang][key];
}
};
const result = await atat.render(template, { lang: 'en' }, options);
<title>@l10n(it.lang, "title")@</title>
<title>@('My Website - ' + $.l10n(it.lang, "title"))@</title>
<!--
Model: { lang: 'en' }
Output:
<title>Main page</title>
<title>My Website - Main page</title>
-->
@json(<object>)@
returns a result of JSON stringify@encode(<string>)@
the same as@(<string>)@
@join(<array>, <separator>)@
joins the array with the separator (optional)@upper(<string>)@
simple uppercase@lower(<string>)@
simple lowercase
@layout(<path>)@
path
the path to the layout file
index.atat
@layout('/views/_layout.atat')@
<div class="page-container">
Home page!
</div>
/views/_layout.atat
<html>
<head></head>
<body>
<main>
@!(body)@
</main>
</body>
</html>
Output:
<html>
<head></head>
<body>
<main>
<div class="page-container">
Home page!
</div>
</main>
</body>
</html>
Patrials allow you to reuse useful pieces of code in different places
@partial(<path>, <model>)@
path
path to partial a view filemodel
model for a partial view (optional)
views/_menu.atat
<nav role="main">
<ul>
<li>
<a href="/" class="@(it.page=='home'?'active':'')@">Home</a>
</li>
<li>
<a href="/about" class="@(it.page=='home'?'active':'')@">About</a>
</li>
</ul>
</nav>
views/_layout.atat
@{ const { $route } = it; }@
<html>
<head></head>
<body>
@partial('/views/_menu.atat', $route)@
<main>
@!(body)@
</main>
</body>
</html>
Output:
<html>
<head></head>
<body>
<nav role="main">
<ul>
<li>
<a href="/" class="active">Home</a>
</li>
<li>
<a href="/about" class="">About</a>
</li>
</ul>
</nav>
<main>
<!-- Home page content -->
</main>
</body>
</html>
Section allows you to pass HTML markup from a view to a layout level
Use the following syntax to specify a new section
@section script {
<script>
document.addEventListener('DOMContentLoaded', function() {
// your code is here
});
</script>
}@
and another one to output the result anywhere
@section(<name>)@
name
sections name
index.atat
@layout('/views/_layout.atat')@
<div class="page-container">
Home page!
</div>
@section script {
<script>
document.addEventListener('DOMContentLoaded', function() {
// your code is here
});
</script>
}@
/views/_layout.atat
<html>
<head></head>
<body>
<main>
@!(body)@
</main>
@section('script')@
</body>
</html>
Output:
<html>
<head></head>
<body>
<main>
<div class="page-container">
Home page!
</div>
</main>
<script>
document.addEventListener('DOMContentLoaded', function() {
// your code is here
});
</script>
</body>
</html>
Just set 'view engine'
value to atat
const express = require('express');
const app = express();
app.set('views', './views');
app.set('view engine', 'atat');
Example available here
The JavaScript Templates script is released under the MIT license.