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
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.
Head
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.
Sidebar
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 ×</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>
Navbar
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">☰</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
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 ].