UNPKG

handlebars-layouts

Version:

Handlebars helpers which implement layout blocks similar to Jade, Jinja, Nunjucks, Swig, and Twig.

443 lines (328 loc) 10.9 kB
# `handlebars-layouts` [![NPM version][npm-img]][npm-url] [![Downloads][downloads-img]][npm-url] [![Build Status][travis-img]][travis-url] [![Coverage Status][coveralls-img]][coveralls-url] [![Tip][amazon-img]][amazon-url] Handlebars helpers which implement layout blocks similar to Jade, Jinja, Nunjucks, Swig, and Twig. ## Install With [Node.js](http://nodejs.org): $ npm install handlebars-layouts With [Bower](http://bower.io): $ bower install shannonmoeller/handlebars-layouts ## API Helpers are generated by passing in your instance of Handlebars. This allows you to selectively register the helpers on various instances of Handlebars. ### `layouts(handlebars) : Object` - `handlebars` `Handlebars` - An instance of Handlebars. Generates an object containing the layout helpers suitible for passing into `registerHelper`. ```js var handlebars = require('handlebars'), layouts = require('handlebars-layouts'); handlebars.registerHelper(layouts(handlebars)); ``` ### `layouts.register(handlebars) : Object` - `handlebars` `Handlebars` - An instance of Handlebars. Both generates an object containing the layout helpers and registers them with Handlebars automatically. ```js var handlebars = require('handlebars'), layouts = require('handlebars-layouts'); layouts.register(handlebars); ``` ## Helpers ### `{{#extend [partial] [context] [key=value ...]}}` - `partial` `String` - Name of partial to render. - `context` `Object` _(Optional)_ - A custom context for the partial. - `attributes` `Object` _(Optional)_ - Arbitrary values that will be added to the partial data context. Loads a layout partial of a given name and defines block content. ```handlebars {{#extend "layout" foo="bar"}} {{#content "title" mode="prepend"}}Example - {{/content}} {{/extend}} ``` The `{{#extend}}` helper allows you to reason about your layouts as you would class extension where the above is equivalent to the following psuedo code: ```js class Page extends Layout { constructor() { this.foo = 'bar'; } title() { return 'Example - ' + super(); } } ``` ### `{{#embed [partial] [context] [key=value ...]}}` - `partial` `String` - Name of partial to render. - `context` `Object` _(Optional)_ - A custom context for the partial. - `attributes` `Object` _(Optional)_ - Arbitrary values that will be added to the partial data context. Allows you to load a partial which itself extends from a layout. Blocks defined in embedded partials will not conflict with those in the primary layout. ```handlebars {{#extend "layout"}} {{#content "body"}} {{#embed "gallery"}} {{#content "body"}} <img src="1.png" alt="" /> <img src="2.png" alt="" /> {{/content}} {{/embed}} {{#embed "modal" foo="bar" name=user.fullName}} {{#content "title" mode="prepend"}}Image 1 - {{/content}} {{#content "body"}}<img src="1.png" alt="" />{{/content}} {{/embed}} {{/content}} {{/extend}} ``` The `{{#embed}}` helper allows you to reason about your partials as you would class instantiation where the above is equivalent to the following psuedo code: ```js class Page extends Layout { body() { var gallery = new Gallery(); gallery.replaceBody('<img src="1.png" alt="" />\n<img src="2.png" alt="" />'); var modal = new Modal({ foo: 'bar', name: this.user.fullName }); modal.prependTitle('Image 1 - '); modal.replaceBody('<img src="1.png" alt="" />'); return gallery.toString() + modal.toString(); } } ``` ### `{{#block [name]}}` - `name` `String` - Block identifier. Defines a named block, with optional default content. Blocks may have content appended, prepended, or replaced entirely when extending or embedding. You may append and prepend to the same block multiple times. ```handlebars {{#block "header"}} <h1>Hello World</h1> {{/block}} {{#block "main"}} <p>Lorem ipsum...</p> {{/block}} {{#block "footer"}} <p>&copy; 1970</p> {{/block}} ``` ### `{{#content [name] mode="(append|prepend|replace)"}}` - `name` `String` - Identifier of the block to modify. - `mode` `String` _(Optional)_ - Means of providing block content. Default: `replace`. Sets block content, optionally appending or prepending using the `mode` attribute. Layout: ```handlebars <html> ... <body> {{#block "header"}} <h1>Hello World</h1> {{/block}} {{#block "main"}} <p>Lorem ipsum.</p> {{/block}} {{#block "footer"}} <p>&copy; 1999</p> {{/block}} </body> </html> ``` Page: ```handlebars {{#extend "layout"}} {{#content "header"}} <h1>Goodnight Moon</h1> {{/content}} {{#content "main" mode="append"}} <p>Dolor sit amet.</p> {{/content}} {{#content "footer" mode="prepend"}} <p>MIT License</p> {{/content}} {{/extend}} ``` Output: ```handlebars <html> ... <body> <h1>Goodnight Moon</h1> <p>Lorem ipsum.</p> <p>Dolor sit amet.</p> <p>MIT License</p> <p>&copy; 1999</p> </body> </html> ``` ### Conditional Blocks There are times where you need to wrap a block with an element or use a different class depending on whether content has been provided for a block. For this purpose, the `content` helper may be called as a [subexpression](http://handlebarsjs.com/expressions.html#subexpressions) to check whether content has been provided for a block. For example, you may wish to have an optional column in a grid layout: ```handlebars {{!-- layout.hbs --}} <div class="grid"> <div class="grid-col {{#if (content "right")}}grid-col_2of3{{else}}grid-col_full{{/if}}"> {{{block "left"}}} </div> {{#if (content "right")}} <div class="grid-col grid-col_1of3"> {{{block "right"}}} </div> {{/if}} </div> ``` For a page that only needs a left column, you may omit defining content for the `right` block: ```handlebars {{!-- page.html --}} {{#extend "layout"}} {{#content "left"}} <p>Left</p> {{/content}} {{/extend}} ``` Resulting in: ```html <div class="grid"> <div class="grid-col grid-col_full"> <p>Left</p> </div> </div> ``` For a page with two columns, simply define content for both blocks: ```handlebars {{!-- page.html --}} {{#extend "layout"}} {{#content "left"}} <p>Left</p> {{/content}} {{#content "right"}} <p>Right</p> {{/content}} {{/extend}} ``` Resulting in: ```html <div class="grid"> <div class="grid-col grid-col_2of3"> <p>Left</p> </div> <div class="grid-col grid-col_1of3"> <p>Right</p> </div> </div> ``` ## Example ### layout.hbs ```handlebars <!doctype html> <html lang="en-us"> <head> {{#block "head"}} <title>{{title}}</title> <link rel="stylesheet" href="assets/css/screen.css" /> {{/block}} </head> <body> <div class="site"> <div class="site-hd" role="banner"> {{#block "header"}} <h1>{{title}}</h1> {{/block}} </div> <div class="site-bd" role="main"> {{#block "body"}} <h2>Hello World</h2> {{/block}} </div> <div class="site-ft" role="contentinfo"> {{#block "footer"}} <small>&copy; 2013</small> {{/block}} </div> </div> {{#block "foot"}} <script src="assets/js/controllers/home.js"></script> {{/block}} </body> </html> ``` ### page.html ```handlebars {{#extend "layout"}} {{#content "head" mode="append"}} <link rel="stylesheet" href="assets/css/home.css" /> {{/content}} {{#content "body"}} <h2>Welcome Home</h2> <ul> {{#items}} <li>{{.}}</li> {{/items}} </ul> {{/content}} {{#content "foot" mode="prepend"}} <script src="assets/js/analytics.js"></script> {{/content}} {{/extend}} ``` ### Putting Them Together ```js var handlebars = require('handlebars'); var layouts = require('handlebars-layouts'); // Register helpers handlebars.registerHelper(layouts(handlebars)); // Register partials handlebars.registerPartial('layout', fs.readFileSync('layout.hbs', 'utf8')); // Compile template var template = handlebars.compile(fs.readFileSync('page.html', 'utf8')); // Render template var output = template({ title: 'Layout Test', items: [ 'apple', 'orange', 'banana' ] }); console.log(output); ``` ### Output (prettified for readability) ```handlebars <!doctype html> <html lang="en-us"> <head> <title>Layout Test</title> <link rel="stylesheet" href="assets/css/screen.css" /> <link rel="stylesheet" href="assets/css/home.css" /> </head> <body> <div class="site"> <div class="site-hd" role="banner"> <h1>Layout Test</h1> </div> <div class="site-bd" role="main"> <h2>Welcome Home</h2> <ul> <li>apple</li> <li>orange</li> <li>banana</li> </ul> </div> <div class="site-ft" role="contentinfo"> <small>&copy; 2013</small> </div> </div> <script src="assets/js/analytics.js"></script> <script src="assets/js/controllers/home.js"></script> </body> </html> ``` ## Contribute [![Tasks][waffle-img]][waffle-url] Standards for this project, including tests, code coverage, and semantics are enforced with a build tool. Pull requests must include passing tests with 100% code coverage and no linting errors. ## Test $ npm test ---- © 2015 Shannon Moeller <me@shannonmoeller.com> Licensed under [MIT](http://shannonmoeller.com/mit.txt) [amazon-img]: https://img.shields.io/badge/tip-jar-yellow.svg?style=flat-square [amazon-url]: https://www.amazon.com/gp/registry/wishlist/1VQM9ID04YPC5?sort=universal-price [coveralls-img]: http://img.shields.io/coveralls/shannonmoeller/handlebars-layouts/master.svg?style=flat-square [coveralls-url]: https://coveralls.io/r/shannonmoeller/handlebars-layouts [downloads-img]: http://img.shields.io/npm/dm/handlebars-layouts.svg?style=flat-square [npm-img]: http://img.shields.io/npm/v/handlebars-layouts.svg?style=flat-square [npm-url]: https://npmjs.org/package/handlebars-layouts [travis-img]: http://img.shields.io/travis/shannonmoeller/handlebars-layouts.svg?style=flat-square [travis-url]: https://travis-ci.org/shannonmoeller/handlebars-layouts [waffle-img]: http://img.shields.io/github/issues/shannonmoeller/handlebars-layouts.svg?style=flat-square [waffle-url]: http://waffle.io/shannonmoeller/handlebars-layouts