Preface

Goal: Build static HTML files using partial Handlebars utilizing Grunt.

Handlebars is interesting, and it looks like having difference approach. Unfortunately, I never really use it in any project. It is an old stuff, but it is new to me, so I cannot say much.

Official Documentation

Source Examples

You can obtain source examples here:


1: Handlebars Case

We use handlebars as html preprocessor to build html documents. Consider transform our last HTML Case into Handlebars Case.

Directory Structure

Consider handlebars structure.

└── views
    ├── css.hbs
    ├── html.hbs
    ├── index.hbs
    ├── javascript.hbs
    ├── partials
    │   ├── head.hbs
    │   ├── layout.hbs
    │   ├── navbar.hbs
    │   └── sidebar.hbs
    └── php.hbs

We want the handlebars to build html artefacts as below:

└── build
    ├── css.html
    ├── html.html
    ├── index.html
    ├── javascript.html
    └── php.html

Handlebars: Example: Tree

Now we can separate each html parts.

Layout

<!DOCTYPE html>
<html>
<head>
{{#> block-head}}
  {{> head title="Link Example: Default" }}
{{/block-head}}
</head>
<body>
  {{> sidebar }}

  <div id="main">
    {{> navbar }}

    <div class="w3-container">
    {{#> block-content}}
      <p>Default Page</p>
    {{/block-content}}
    </div>
  </div>

</body>
</html>

As shown in order we have three include artefacts.

  • {{> head title="Link Example: Default" }},
  • {{> sidebar }},
  • {{> navbar }},

And one block named block-content.

{{#> block-content}}
  <p>Default Page</p>
{{/block-content}}

This weird {{> ... }} notation looks cool. At least for me.

Handlebars: ViM NERDTree Panes

It is almost the same with the original head.

  <title>{{ title }}</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
  <script src="../js/script.js"></script>
  <script src="//localhost:35729/livereload.js"></script>

The title variable is imply written as:

<title>{{ title }}</title>

I also add livereload for use with grunt later.

It is a simply a verbatim copy-paste from the original html.

  <div class="w3-sidebar w3-bar-block w3-card w3-animate-left" 
       style="display:none" id="mySidebar">
    <button class="w3-bar-item w3-button w3-large"
       id="closeNav" >Close &times;</button>
    <a class="w3-bar-item w3-button"
       href="index.html">Home</a>
    <a class="w3-bar-item w3-button"
       href="html.html">HTML Link</a>
    <a class="w3-bar-item w3-button"
       href="css.html">CSS Link</a>
    <a class="w3-bar-item w3-button"
       href="php.html">PHP Link</a>
    <a class="w3-bar-item w3-button"
       href="javascript.html">Javascript Link</a>
  </div>

It is also a an exact copy from the original html.

    <div class="w3-teal">
      <button class="w3-button w3-teal w3-xlarge"
              id="openNav">&#9776;</button>
      <div class="w3-container">
        <h1>My Page</h1>
      </div>
    </div>

    <div class="w3-bar w3-black">
      <a class="w3-bar-item w3-button"
         href="index.html">Home</a>
      <a class="w3-bar-item w3-button"
         href="html.html">HTML</a>
      <a class="w3-bar-item w3-button"
         href="css.html" >CSS</a>
      <a class="w3-bar-item w3-button"
         href="php.html" >PHP</a>
      <a class="w3-bar-item w3-button"
         href="javascript.html">Javascript</a>
    </div>

Index

By including the partials/layout.hbs, we can pass both the variable title and content.

{{#> layout }}

  {{#*inline "block-head"}}
    {{> head title="Link Example: Home" }}
  {{/inline}}

  {{#*inline "block-content"}}
    <p>Home Page</p>
  {{/inline}}

{{/layout}}

2: Handlebars Content Propagation

How does it works ?

Block and Inheritance

In partials/layout.njk we have this block.

    {{#> block-content}}
      <p>Default Page</p>
    {{/block-content}}

Then we can replace the content, of the block in child layout index.njk.

  {{#*inline "block-content"}}
    <p>Home Page</p>
  {{/inline}}

Title

Variable Propagation Between Template

In index.hbs we have this block-head.

  {{#*inline "block-head"}}
    {{> head title="Link Example: Home" }}
  {{/inline}}

And in partials/layout.hbs, we change the content of block-head, and also pass the variable title.

{{#> block-head}}
  {{> head title="Link Example: Default" }}
{{/block-head}}

This title variable will be utilized in partials/head.njk

  <title>{{ title }}</title>

This is almost self explanatory.

Main Files

With the wisdom of copy-paste knowledge, all the main files can be so short as below:

{{#> layout }}

  {{#*inline "block-head"}}
    {{> head title="Link Example: CSS" }}
  {{/inline}}

  {{#*inline "block-content"}}
    <p>CSS Page</p>
  {{/inline}}

{{/layout}}
{{#> layout }}

  {{#*inline "block-head"}}
    {{> head title="Link Example: PHP" }}
  {{/inline}}

  {{#*inline "block-content"}}
    <p>PHP Page</p>
  {{/inline}}

{{/layout}}

Result

As usual, check build/index.html against the original html.

<!DOCTYPE html>
<html>
<head>
      <title>Link Example: Home</title>
      ...
</head>
<body>
    ...

  <div id="main">
    ...

    <div class="w3-container">
    <p>Home Page</p>
    </div>
  </div>

</body>
</html>

3: Grunt Configuration

Command Line Interface

I should remove this CLI section, as I already have this good grunt.

package.json

All you need to know is the devDependencies.

  "devDependencies": {
    "grunt": "^1.0.1",
    "grunt-compile-handlebars": "^2.0.2",
    "grunt-contrib-watch": "^1.1.0",
    "handlebars": "^4.7.6"
  }

To begin with grunt, you need to run npm install first.

Gruntfile.js

You also need to a configuration file named Gruntfile.js.

module.exports = function(grunt) {
  // configure the tasks
  let config = {

    'compile-handlebars': {
      home: {
        files: [ {
          cwd: "views",
          src: "*.hbs",
          dest: "build",
          expand: true,
          ext: ".html"
        } ],
        partials: 'views/partials/*.hbs'
      }
    },

    watch: {
      handlebars: {
        files: ['views/**/*'],
        tasks: ['compile-handlebars'],
        options: {
          livereload: true,
          interrupt: false,
          spawn: false
        }
      }
    }

  };

  grunt.initConfig(config);

  // load the tasks
  grunt.loadNpmTasks('grunt-compile-handlebars');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // define the tasks
  grunt.registerTask('default', [
    'compile-handlebars', 'watch'
  ] );

};

The configuration is not exactly explanatory. In fact, I found it hard to on my first attempt. After stackoverflowing for sometime, I finally got the clue. The key is to register views/partials/*.hbs.

This difficulties is also an issue in the next Express article. But do not worry, I have made this ready to use configuration for you. You should read the official documentation of grunt-compile-handlebars.

Running Grunt

Handlebars: Grunt: Watch

Now you can build your own static html files using handlebars.


What’s Next?

Now that we are done with grunt, we will continue with ExpressJS. Consider continue reading [ Template - Express - Pug ].