Let's build something fun with the Handlebars.JS in this blog. Source files used in this tutorial series can be found on Github

Basics

Initilize your project

npm init
npm install --save express hbs
# Create and organize the file structure like this (folders do not have extenstion names)
- node_modules
- views
	- partials
		- nav.hbs
	- layout.hbs
	- home.hbs
	- page1.hbs
	- page2.hbs
- app.js (npm init uses index.js as main entry by default, you can change it in npm init)
- package.json
- package-lock.json

Main entry - app.js

const app = require('express')()
const path = require('path')
const hbs = require('hbs')
const port = process.env.PORT || 3000

// Configurations
hbs.registerPartials(__dirname + '/views/partials')
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'hbs')

// Routings
app.get('/', (req, res) => {
    res.render('home', {
        title: 'Home',
        content: 'I am a demo text from home page'
    })
})

app.get('/1', (req, res) => {
    res.render('page1', {
        title: 'Page 1',
        content: 'I am a demo text from page 1'
    })
})

app.get('/2', (req, res) => {
    res.render('page2', {
        title: 'Page 2',
        content: 'I am a demo text from page 2'
    })
})

app.listen(port, () => {
    console.log('Server is started')
    console.log(`Open http://localhost:${port} in your browser`)
})
  • Pay attention to things below the comment called Configurations. They are necessory to be set properly for handlebars
  • In the render, you can see a json-structure data that contains variables. These variables will  be sent to associated template to render the view via response
  • home, page1 and page2 in the routings are linked to the template files with .hbs at the end

Entry Handlebars Template - layout.hbs

  • With Handlebars enabled, we need a template called layout to define how the whole site looks like.
  • In this example, I put the basic elements, including html tag, header and so on, from a regular website to the layout.hbs
  • Things wraped by curly brackets are called variables
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{title}}</title>
    <style>
        html, body {
            width: 100%;
            margin: 0;
        }

        a {
            color: #c4c4c4;
            text-decoration: none;
            transition: 0.2s;
            -webkit-transition: 0.2s;
        }

        a:hover {
            color: #FFFFFF;
        }
        
        body {
            padding: 50px 12px 0px 12px;
        }

        #NavBar {
            background: #2c2c2c;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 50px;
            line-height: 50px;
            margin: 0;
        }

        #NavBar li {
            display: inline-block;
            padding: 0px 12px 0px 12px;
        }
    </style>
</head>

<body>
    {{>nav}}
    {{{body}}}
</body>

</html>
  • {{>nav}} stands for the nav template inside the partials directory
  • {{{body}}} stands for the section where main templates like home.hbs and page1.hbs will be injected into. This is controlled by the render in the app.js

Partial Handlebars Template - nav.hbs

<ul id="NavBar">
    <li><a href="/">Home</a></li>
    <li><a href="/1">Page 1</a></li>
    <li><a href="/2">Page 2</a></li>
</ul>
  • Will be inserted to every page since it presents as a partial template used in the layout

Main Handlebars Templates - home.hbs, page1.hbs, page2.hbs

  • To keep things simple, I used only 1 variable for all 3 templates
  • These variables accept json-structure data sent from the render in the app.js

home.hbs

<h1>This is Home page</h1>
<p>{{content}}</p>

page1.hbs

<h1>This is Page 1</h1>
<p>{{content}}</p>

page2.hbs

<h1>This is Page 2</h1>
<p>{{content}}</p>

Partial Handlebars Template - nav.hbs

<ul id="NavBar">
    <li><a href="/">Home</a></li>
    <li><a href="/1">Page 1</a></li>
    <li><a href="/2">Page 2</a></li>
</ul>

Try it by yourself

git clone https://github.com/Landon-Dou/handlebars-tutorials.git
cd Basics
npm install
npm start

Conditions

If helper

# home.hbs
...
{{#if isLoggedIn}}
<h5>Welcome back, 100001</h5>
<h5>here's the latest news</h5>
<h5>...</h5>
{{else}}
<h5>Click here to log In</h5>
{{/if}}
...

# app.js
...
res.render('home', {
	title: 'Home',
	content: 'I am a demo text from home page',
	isLoggedIn: false
})
...
  • When the variable sent from the res.render is a boolean, we can use condition with if helper and unless helper
  • In this case, when the user is not logged in, the button to log in will be displayed, otherwise the content of user will be displayed

Unless helper

# home.hbs
...
{{#unless isLoggedIn}}
<h5>Ads</h5>
{{/unless}}
...
  • unless is a way to declare the condition of if-not. In this example, ads only shows when the user is not logged in.
  • It can be translated into "I will display Ads unless the user isLoggedIn"

List

Each helper

# home.hbs
...
{{#each content}}
<p>{{this}}</p>
{{/each}}
...
# exacltly same as
...
{{#each content}}
<p>{{.}}</p>
{{/each}}

# app.js
...
res.render('home', {
	title: 'Home',
    content: [
        'Facebook just launched a rocket to the moon on sunday',
        'iPhone XXL has a new feature called dancing mode',
        'Shocking report exposes hidden secret of the universe'
    ],
    isLoggedIn: true
})
...

Or we can use it to find each element of object insead of a list

# home.js
...
{{#each user as |value key|}}
<p>{{key}} : {{value}}</p>
{{/each}}
...

# app.js
...
res.render('home', {
	...
	user: {
		id: '100000',
		notifications: 0,
		email: 'example@example.com'
	}
	...
})
...
  • The value and the key are renamable variables