• Stars
    star
    359
  • Rank 118,537 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 11 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

handlebars-layouts

NPM version Downloads Build Status Coverage Status Tip

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

Install

With Node.js:

$ npm install handlebars-layouts

With Bower:

$ 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.

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.

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.

{{#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:

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.

{{#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:

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.

{{#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:

<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:

{{#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:

<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 to check whether content has been provided for a block.

For example, you may wish to have an optional column in a grid layout:

{{!-- 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:

{{!-- page.html --}}
{{#extend "layout"}}

    {{#content "left"}}
        <p>Left</p>
    {{/content}}

{{/extend}}

Resulting in:

<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:

{{!-- page.html --}}
{{#extend "layout"}}

    {{#content "left"}}
        <p>Left</p>
    {{/content}}

    {{#content "right"}}
        <p>Right</p>
    {{/content}}

{{/extend}}

Resulting in:

<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

<!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

{{#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

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)

<!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

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

MIT © Shannon Moeller

More Repositories

1

reset-css

An unmodified* copy of Eric Meyer's CSS reset. PostCSS, webpack, Sass, and Less friendly.
Less
333
star
2

up

Quickly navigate to a parent directory via tab-completion.
Shell
160
star
3

gulp-hb

A sane Gulp plugin to compile Handlebars templates. Useful as a static site generator.
JavaScript
147
star
4

ygor

Task toolkit. For when `npm run` isn't enough and everything else is too much.
JavaScript
68
star
5

find-config

Like findup-sync, but 2-4x faster and supports XDG-style `.config/` directories.
JavaScript
40
star
6

handlebars-wax

The missing Handlebars API for data, partials, helpers, and decorators.
JavaScript
38
star
7

regx

Tagged-template-string regular-expression compiler.
JavaScript
34
star
8

front-end-logo

A community logo for front-end web development.
32
star
9

cli-columns

Columnated lists for the CLI. Unicode and ANSI safe.
JavaScript
31
star
10

require-glob

Requires multiple modules using glob patterns and combines them into a nested object.
JavaScript
21
star
11

mute

Politely tells stdout and stderr to shut the heck up for a moment.
JavaScript
11
star
12

handlebars-group-by

Handlebars helper which allows you to group lists by a property of each item.
JavaScript
10
star
13

spiff

Promise-aware file-system adapter and transmogrifier.
JavaScript
9
star
14

apply-html

It's `.innerHTML = ''` for the 21st century.
JavaScript
7
star
15

livery

CLI to reload browsers when files change with LiveReload.
JavaScript
7
star
16

todomvc-custom-elements

TodoMVC app built using Custom Elements and up-and-coming vanilla code in the browser and on the server.
JavaScript
7
star
17

bulk

Executes a command across multiple subdirectories.
Shell
7
star
18

deduce

Ridiculously easy JavaScript state containers with action methods.
JavaScript
7
star
19

pardy

Websocket-based trivia game.
JavaScript
6
star
20

whim

A protean Node.js toolset based on my mercurial preferences.
JavaScript
6
star
21

esprev

It's Babel without the setup pain. Like the original 6to5.
JavaScript
5
star
22

dotfiles

.files 📂
Shell
5
star
23

run-headless

The easiest way to run code in a headless browser.
JavaScript
5
star
24

rollup-preset-isomorphic

Everything you need to rollup isomorphic modules.
JavaScript
4
star
25

talks

Supporting material for my public presentations.
JavaScript
4
star
26

list-promise

Maximally-concurrent Promise-based array iteration.
JavaScript
4
star
27

gulp-promisify

Enables use of Promises or async/await keywords to control the flow of Gulp tasks.
JavaScript
4
star
28

vanilla-mvc-demo

Demo MVC app written in vanilla JS, with dependency injection, services, templates, and factories.
JavaScript
3
star
29

todomvc-vanilla

A TodoMVC example using minimal tooling and evergreen vanilla features.
JavaScript
3
star
30

babel-browserify-nyc-tape-zuul-isomorphic-demo

An isomorphic JavaScript development stack with tests and code coverage using babel, browserify, nyc, tape, and zuul.
JavaScript
3
star
31

code

code 🗃️
JavaScript
2
star
32

vanilla-zero

Modern web development with zero calories.
CSS
2
star
33

trivia

trivia 🧐
JavaScript
2
star
34

isomorphic-boilerplate

Isomorphic JS project boilerplate using babel, browserify, gulp, and tape.
JavaScript
1
star
35

async-map-stream

JavaScript
1
star
36

get-context

Chainable canvas context api wrapper.
JavaScript
1
star
37

vinyl-rw

A file-system aware vinyl file with first-class string support.
JavaScript
1
star
38

enceladus

Game Off 2020
JavaScript
1
star
39

isitchristmas

Is it Christmas?
HTML
1
star
40

infinite-improbability-drive

Infinite Improbability Drive
JavaScript
1
star
41

say-repl

A read-eval-print loop for the macOS `say` command.
JavaScript
1
star
42

advent-of-code

JavaScript
1
star