iopa-template-handlebars
v0.9.3
Published
Handlebars Template Engine for the IOPA Stack for Node.js
Downloads
55
Readme
iopa-template-handlebars
About
iopa-template-handlebars
is IOPA middleware for rendering templates using handlebars template engine
Installation
$ npm install iopa-template-handlebars
Credits
This project, including documentation, was forked under BSD license from the Yahoo ericf/express-handlebars with various changes throughout to map to IOPA vs Express.
To date, the raw parsing routines have not been adjusted materially from the Yahoo implementation.
Goals and Features
After building a half-dozen Express apps, the original author developed requirements and opinions about what a Handlebars view engine should provide and how it should be implemented. The following is that list:
Add the concept of "layout"
Add the concept of "partials" via Handlebars' partials mechanism.
Support a directories of partials; e.g.,
{{> foo/bar}}
which exists on the file system atviews/partials/foo/bar.handlebars
, by default.Smart file system I/O and template caching. When in development, templates are always loaded from disk. In production, raw files and compiled templates are cached, including partials.
All async and non-blocking. File system I/O is slow and servers should not be blocked from handling requests while reading from disk. I/O queuing is used to avoid doing unnecessary work.
Ability to easily precompiled templates and partials for use on the client, enabling template sharing and reuse.
Ability to use a different Handlebars module/implementation other than the Handlebars npm package.
Package Design
This package was designed to work great for both the simple and complex use cases. We intentionally made sure the full implementation is exposed and is easily overridable.
The package exports a function which can be invoked with no arguments or with a config
object and it will return a function (closed over sane defaults) which can be registered with an IOPA app. It's an engine factory function.
This exported engine factory has four properties which expose the underlying implementation:
(default): The simplest IOPA middleware to use in
app.user(handlebars)
;IopaHandlebars()
: The constructor function which holds the internal implementation on itsprototype
. This produces instance objects which store their configuration,compiled
andprecompiled
templates, and expose anengine()
function which can be registered with an IOPA app.engine()
: A convenience factory function for creatingIopaHandlebars
instances.
An instance-based approach is used so that multiple IopaHandlebars
instances can be created with their own configuration, templates, partials, and helpers.
Installation
Install using npm:
$ npm install express-handlebars
Usage
This view engine uses sane defaults that leverage the "IOPA-way" of structuring an app's views. This makes it trivial to use in basic apps:
Basic Usage
Directory Structure:
.
├── app.js
└── views
├── home.hbs
└── layouts
└── main.hbs
2 directories, 3 files
app.js:
Creates a super simple IOPA app which shows the basic way to register a Handlebars view engine using this package.
const iopa = require('iopa'),
templates = require('iopa-templates'),
handlebars = require('iopa-template-handlebars'),
iopaConnect = require('iopa-connect'),
http = require('http'),
var app = new iopa.App();
app.use(templates);
app.engine('.hbs', handlebars({defaultLayout: 'main', views: 'test/views'}));
app.use(function(context, next) {
return context.render('home.hbs');
});
http.createServer(app.buildHttp()).listen(3000);
views/layouts/main.handlebars:
The main layout is the HTML page wrapper which can be reused for the different views of the app. {{{body}}}
is used as a placeholder for where the main content should be rendered.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example App</title>
</head>
<body>
{{{body}}}
</body>
</html>
views/home.handlebars:
The content for the app's home view which will be rendered into the layout's {{{body}}}
.
<h1>Example App: Home</h1>
Running the Example
The above example is bundled in this package's demo.js
, where it can be run by:
$ node demo
Template Caching
This view engine uses a smart template caching strategy. In development, templates will always be loaded from disk, i.e., no caching. In production, raw files and compiled Handlebars templates are aggressively cached.
Layouts
A layout is simply a Handlebars template with a {{{body}}}
placeholder. Usually it will be an HTML page wrapper into which views will be rendered.
This view engine supports the concept of "layout". It can be configured with a path to the layouts directory, by default it's set to "views/layouts/"
.
The layout into which a view should be rendered can be overridden per-request by assigning a different value to the layout
request local. The following will render the "home" view with no layout:
app.get('/', function (context) {
context.render('home', {layout: false});
});
Helpers
Helper functions, or "helpers" are functions that can be registered with Handlebars and can be called within a template. Helpers can be used for transforming output, iterating over data, etc. To keep with the spirit of logic-less templates, helpers are the place where logic should be defined.
Handlebars ships with some built-in helpers, such as: with
, if
, each
, etc. Most application will need to extend this set of helpers to include app-specific logic and transformations. Beyond defining global helpers on Handlebars
, this view engine supports IopaHandlebars
instance-level helpers via the helpers
configuration property, and render-level helpers via options.helpers
when calling the render()
and renderView()
methods.
The following example shows helpers being specified at each level:
app.js:
Creates a super simple IOPA app which shows the basic way to register IopaHandlebars
instance-level helpers, and override one at the render-level.
const iopa = require('iopa'),
templates = require('iopa-templates'),
handlebars = require('./index.js'),
http = require('http'),
iopaConnect = require('iopa-connect')
router = require('iopa-router'),
helpers = require('./test/advanced/lib/helpers');
var app = new iopa.App();
app.use(templates);
app.use(router);
app.engine('.hbs', handlebars({
defaultLayout: 'main',
views: 'test/advanced/views',
helpers : helpers,
partialsDir: [
'shared/templates/',
'views/partials/'
]}));
app.get('/', function (context) {
return context.render('home', {
title: 'Home'
});
});
app.get('/yell', function (context) {
return context.render('yell', {
title: 'Yell',
// This `message` will be transformed by our `yell()` helper.
message: 'hello world'
});
});
app.get('/exclaim', function (context) {
return context.render('yell', {
title : 'Exclaim',
message: 'hello world',
// This overrides _only_ the default `yell()` helper.
helpers: {
yell: function (msg) {
return (msg + '!!!');
}
}
});
});
var port = process.env.PORT || 3000;
http.createServer(app.buildHttp()).listen(port);
console.log('listening at:', port);
views/home.handlebars:
The app's home view which uses helper functions to help render the contents.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example App - Home</title>
</head>
<body>
<!-- Uses built-in `if` helper. -->
{{#if showTitle}}
<h1>Home</h1>
{{/if}}
<!-- Calls `foo` helper, overridden at render-level. -->
<p>{{foo}}</p>
<!-- Calls `bar` helper, defined at instance-level. -->
<p>{{bar}}</p>
</body>
</html>
More on Helpers
Refer to the Handlebars website for more information on defining helpers:
Metadata
Handlebars has a data channel feature that propagates data through all scopes, including helpers and partials. Values in the data channel can be accessed via the {{@variable}}
syntax. IOPA Handlebars provides metadata about a template it renders on a model object allowing access to things like the view name passed to context.render()
The following is the list of metadata that's accessible on the data object:
cache
: Boolean whether or not the template is cached.view
: String name of the view passed tores.render()
.layout
: String name of the layout view.data
: Original data object passed when rendering the template.helpers
: Collection of helpers used when rendering the template.partials
: Collection of partials used when rendering the template.