HYbrid TEmplating for the browser and Node. Using Hogan.js (based on mustache) as templating engine.
- Introduction
- Installation
- Templating 101
- Pre-compiled templates
- Quick overview: hyte
- Services
- CLI Options
- Tests
- Annotated source code
- Shouts / dependencies
The most important reason for me to develop hyte was to learn. To learn and gain experience in a couple of areas, including:
- Pre-compiled templates
- Reusing templates in Node & in the browser
- Develop for Node
- npm packaging
- Complete project offering:
- Works out of the box
- Documentation
- Tests
Although learning was the main goal, this project still contains lots of interesting and working concepts and demonstrations. Please feel free to use anything from this project in any way you see fit.
npm install
node server.js
You can now browse to http://localhost:3000/index.html
for a demo.
Most JavaScript templating engines do their work in two phases:
- compile the template
- in: template
- out: compiled template as a JavaScript function
- render the template
- in: compiled template + data
- out: populated template, usually HTML
Hogan, the basis for hyte, also works like this. So, a Mustache template like this..
<h1>{{title}}</h1>
<ul>
{{#names}}
<li>{{name}}</li>
{{/names}}
</ul>
..can be compiled..
var template = hogan.compile('<h1>{{title}}</h1><ul>{{#names}}<li>{{name}}</li>{{/names}}</ul>');
..resulting in a JavaScript function..
function(c,p,i){var _=this;_.b(i=i||"");_.b("<h1>");_.b(_.v(_.f("title",c,p,0)));_.b("</h1>");_.b("\n" + i);_.b("<ul>");_.b("\n" + i);if(_.s(_.f("names",c,p,1),c,p,0,35,56,"{{ }}")){_.rs(c,p,function(c,p,_){_.b("<li>");_.b(_.v(_.f("name",c,p,0)));_.b("</li>");_.b("\n");});c.pop();}_.b("</ul>");return _.fl();;}
..which can be passed some data..
var data = {
"title": "Story",
"names": [
{"name": "Tarzan"},
{"name": "Jane"}
]
}
..and it will render..
var result = template.render(data);
..into this result:
<h1>Story</h1>
<ul>
<li>Tarzan</li>
<li>Jane</li>
</ul>
Since compilation must be done anyway, and it can be done on the server, this is definitely the recommended way to go. Most importantly, it takes away processing on the client, which is great for performance. Additionally, the compiled JS functions serve as a cache in itself. The JS being served to the client can look like this:
var myApp.templates = {
template1: new Hogan.Template(function(c,p,i){var _=this;_.b(i=i||"");_.b("<h1>");_.b(_.v(_.f("title",c,p,0)));_.b("</h1>");_.b("\n" + i);_.b("<ul>");_.b("\n" + i);if(_.s(_.f("names",c,p,1),c,p,0,35,56,"{{ }}")){_.rs(c,p,function(c,p,_){_.b(" <li>");_.b(_.v(_.f("name",c,p,0)));_.b("</li>");_.b("\n");});c.pop();}_.b("</ul>");return _.fl();;}),
template2: new Hogan.Template(function(c,p,i){var _=this;_.b(i=i||"");_.b("<p>");_.b(_.v(_.f("message",c,p,0)));_.b("</p>");_.b("\n");return _.fl();;})
}
Any pre-compiled template can then be used like this:
var result = myApp.templates['template1'].render(data);
document.getElementById('placeholder').innerHTML = result;
Depending on the situation, even the template rendering can be done server-side. When the data is, or can be made available server-side, this would even further reduce processing on the client, serving pre-rendered HTML to the client. This can be inserted in the DOM right away.
The included hyte server provides:
- All your pre-compiled templates at
/compiled.js
- Pre-compile separate templates via
/compile/[template]
- Server-side rendering of templates via
/render/[template]/[encoded-endpoint-url]
- Render template server-side using POST data at
/render/[template]
The hyte Node module can also be used stand-alone:
hyte.compile(template, callback)
hyte.compileAll(callback)
hyte.render(template, data, callback)
hyte.renderFromEndpoint(template, dataURI, callback)
On the client, pre-compiled templates can be rendered...
var renderedTemplate = compiledTemplate.render(data);
...or just, when pre-rendered on the server...
$('#placeholder).html(renderedTemplate);
Running the server will give you services at http://localhost:3000
to:
All templates at /public/views/*.html
are pre-compiled into JS and ready at /compiled.js
.
Example result for /compiled.js (object property keys directly taken from filename):
window.app.templates = {
"list": new Hogan.Template(function(c,p,i){}),
"paragraph": new Hogan.Template(function(c,p,i){})
};
This can then be used like this:
var data = {"message": "This is rendered client-side in a pre-compiled template"}
var html = app.templates['list'].render(data);
$('#placeholder').append(html);
The file /compiled.js
itself is using a compilation template (lib/compilationSet.default.mustache
).
Location: GET http://localhost:3000/compiled.js
Suggested usage: make sure the first and/or most used templates are pre-compiled like this.
Pre-compiled templates are available at /compile/[template]
. Compiled to JS in AMD style from /public/views/[template].html
, using the compilation template at lib/compilation.amd.mustache
:
define(new Hogan.Template(function(c,p,i){}))
So you can use it like this:
require(['/compile/paragraph'], function(compiledTemplate) {
var html = compiledTemplate.render({"message": "This is rendered client-side in a pre-compiled template"});
$('#placeholder').append(html);
});
Location: GET http://localhost:3000//compile/[template]
Suggested usage: this usage can come in handy when the data is available client-side, and the template still needs to be fetched and compiled.
By providing a reference to the template and a full URL to the data endpoint, this service returns pre-rendered templates.
For example:
http://localhost:3000/render/paragraph/http%3A%2F%2Flocalhost%3A3000%2Fdata%2Fparagraph.json
returns an HTML string ready to be inserted in the page:
<p>This is data in a JSON resource</p>
For now, the data endpoint should deliver JSON that is tailored to the template (no intermediate parsing of data).
Location: GET http://localhost:3000/render/[template]/[encoded-endpoint-url]
Suggested usage: this is the best usage performance-wise, just make sure template and data (JSON) match directly.
Get pre-rendered HTML from server by providing reference to template and POST data (no processing client-side)
var data = {"message": "This is pre-rendered server-side"};
$.post('/render/paragraph', data, function(renderedTemplate) {
$('#placeholder').append(renderedTemplate);
});
Location: POST http://localhost:3000/render/[template]
Suggested usage: the data is available client-side, but the compiled template is not.
Static files from /public
are available from the root url. E.g. /index.html
is served from /public/index.html
.
Files from the template directory are watched for changes, and then automatically re-compiled.
To enable this template file watcher (to recompile templates when it changes):
node server.js --watcher
See node server.js --help
for all CLI options.
Run tests using mocha
make test
Generate test coverage report to docs/coverage.html (requires node-jscoverage)
make test-cov
Generate annotated source code in docs folder (using Docco)
make docs
Many thanks to anyone that contributed to the libraries used to build this tool (in alphabetical order):