Handgrip is a simple wrapper the enables the use of generator inside normal Handelbars helper.
In addition to supporting generator functions, Handgrip keeps the order of executing generator helpers as you write them on template.
Best suited/tested with Koa or co.
$ npm install handgrip
This is an example of basic usage of generator helper.
Return a generator function with next
as the single argument, just like Koa’s middleware.
yield next
tells Handgrip to continue parsing the rest of the template.
var hbs = require("handgrip");
var request = require("co-request");
hbs.registerGeneratorHelper("ip", function() {
return function *(next) {
var ip = yield request("http://canihazip/s");
yield next;
return new hbs.SafeString(ip);
};
});
Another interesting example of generator helper is to create composable component with Handgrip/Handlebars helpers. Here, a CSS declaration helper will keep tracks of all component CSS on this page.
<!doctype html>
<html>
<head>
{{css "bootstrap"}}
</head>
<body>
<nav>...</nav>
{{component "list"}}
{{component "carousel"}}
</body>
</html>
var hbs = require("handgrip");
hbs.registerGeneratorHelper({
// gathering all component CSS and write it in <head>
css: function(name) {
this._CSS = name.split(",");
return function*(next) {
yield next;
var css = this._CSS.reduce(function(a, b) {
return a + '<link href="' + b + '" rel="stylesheet" />';
}, "");
return new hbs.SafeString( css );
};
},
// run component logic and
// report its CSS
component: function(name) {
return function *(next) {
this._CSS = this._CSS || [];
if (name) this._CSS.push(name);
// insert your magic here
...
yield next;
return new hbs.SafeString( ... );
}
}
});
Nested generator helper is also supported. Checkout test/
folder to see more example on that.
registerGeneratorHelper
does support normal Handlebars helper as well, just like using the plain Handlebars. Here is an example of registered multiple helper
var hbs = require("handgrip");
var request = require("co-request");
hbs.registerGeneratorHelper({
hello: function(name) {
var out = '<b>' + name + '</b> says hello.';
return new hbs.SafeString(out);
},
ip: function() {
return function *(next) {
var ip = yield request("http://canihazip/s");
yield next;
return new hbs.SafeString(ip);
}
}
});
var handgrip = require("handgrip");
var tpl = handgrip.render(template, options);
var body = yield tpl(data);
Handgrip.render
accepts the same parameters and Handlebars.compile
and returns a generator function.
var handlebars = require("handlebars");
var handgrip = require("handgrip");
// create a compile templateFn
// You could use handgrip.compile(...) as well
var tpl = handlebars.compile(template);
// convert to to Handgrip-compatible
var renderer = Handgrip.createRenderer(tpl);
var body = yield renderer(data);
Handgrip.compile
is the same as Handlebars.compile
.
Handgrip keeps an array of generator functions for each render
job.
When Handlebars finishes parsing the template, it replaces those generator helper with placeholder of UUIDs. Then Handgrip begins parsing the generator array and mutate the array as new generator helpers are found, finally Handgrip fills the results back to their corresponding places.
The common way of implementing layout (as found in express-hbs and koa-hbs) is to render the template as string, then create another separate rendering process for layout. Unfornately, this trivial approach is not feasbile with the implementation of Handgrip, since splitting the rendering process means losing track of generator helpers in main template.
However, there is also an easy way of implementing layout with Handgrip.
Layout template:
<!doctype html>
<html>
<head>
{{css}}
{{js}}
...
</head>
<body>
{{__body__}}
</body>
</html>
Main template:
{{registerCSS "foo"}}
{{registerJS "bar"}}
{{pageTitle}}
{{#layout "layoutName"}}
<!-- your content goes here -->
...
{{/layout}}
Layout helper (JS):
var hbs = require("handgrip");
hbs.registerHelper("layout", layout);
function layout(layoutName, options) {
this.__body__ = options.fn.bind(null, this);
// read layout file into template string
var layoutTpl = findByName(layoutName);
// this is the same as handlebars.compile(...)
return hbs.compile(layoutTpl, this);
}
This is normally used on the server side, so there is no minified version (yet).
Tested on
- Handlebars: 2.0.2, 3.0.3, 4.0.5
- co: 3.0+, 4.0+
- Node: 0.11.14, 4.4.x, 5.x, 6.x
- iojs: 1.0.2 or higher
Run the test
$ npm install
$ npm test
Copyright (c) 2015 Jingwei "John" Liu
Licensed under the MIT license.