handgrip

0.4.0 • Public • Published

Handgrip: Enable generator helpers for Handlebars

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.

Build Status NPM version License

Best suited/tested with Koa or co.

Install

$ npm install handgrip

Generator Helpers

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.

Normal Helpers

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);
        }
    }
});

Compiling

Compile template string

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.

Convert Handblebars compiled templateFn to a Handgrip-compatible one

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.

Implementaion and caveats (Must-see for layout users)

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);
}

Build

This is normally used on the server side, so there is no minified version (yet).

Tests

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

License

Copyright (c) 2015 Jingwei "John" Liu

Licensed under the MIT license.

Readme

Keywords

none

Package Sidebar

Install

npm i handgrip

Weekly Downloads

13

Version

0.4.0

License

MIT

Unpacked Size

17.6 kB

Total Files

10

Last publish

Collaborators

  • th507