In this chapter I am going to discuss how to work with templating engines. Specifically, I am going to explain how to work with a templating engine called Handlebars.


Table of Contents:


After completing chapter 1, you should have a working Node.js project that returns Hello World! . The structure of the project should look like this:

You can find the final result of this chapter in Github.

microblog/
	node_modules/
	app.js
	package.json
	package-lock.json

In order to start the application you navigate to the root directory of the project and then you execute in your terminal node app.js. This will start a web server along with the application and therefore when you visit localhost:3000 the server will return the Hello World! sentence that we defined earlier.

In this chapter we are going to look how to create dynamic html pages using templates. This means that the content of the website that we are going to build can change accordingly to our business logic. If something is unclear so far you can go back to chapter 1, or checkout the code for chapter 1 and continue to chapter 2.

Templates Introduction

We know so far that web browsers (e.g Firefox, Chrome) can process html, css and javascript files. I would like for PicoBlog to have a way of displaying the logged in user. At the moment the implementation of user login is missing but we can fake it (temporarily). I am going to represent the user details with a json object (at least for now) and use string concatenation to send html back to the browser. Lets say the user that is logged in is me and my user details are:

user = {
    username: 'conpiy'
}

Mocking the user details is a really good way to concentrate on what is important at each step. For example now, we would like to design the home page of our website and we don't want the fact that we don't have a user access system in place to hold us back. Therefore, creating a mock like this is a common technique and we will use it many times in this tutorial.  Now we would like to return a simple html page. When the user loads the homepage it will display the user details.

Let's see the code and explain what everything means:

const express = require('express');
const app = express();
const port = 3000;

user = {
    username: 'conpiy'
}

app.get('/', (req, res) => res.send(`
<html>
    <head>
        <title>PicoBlog</title>
    </head>
    <body>
        <h1>Logged in as, ${user.username} </h1>
    </body>
</html>`));

app.listen(port, () => console.log(`Server listening at: ${port}!`));

I am not going to cover the basics of html on this course. If you like to learn more about html you can look here.

So, as I described earlier we mocked the user details and using string concatenation the express application is returning valid html markup to the browser. Now if you run the app with node app.js and navigate from your browser to localhost:3000 you should be able to see the updated home page. It should look like this:

Chapter 2 - Homepage

As you can see in the previous code snippet, writing html mixed with your logic is not ideal. In fact is terrible. If you can take a step back and imagine where this application is going. In the future, it will have multiple javascript files and multiple routes. For example we might want to have a page that you can see blog posts and another page for each user's account details. Writing html inside our code will make this project harder to scale and maintain and also really difficult to add new features.

The solution is to keep our presentation layer separate from our business logic. Templates are doing just that. It's a way of creating the html pages in a way that we can pass data from our code to our html pages without having to put everything in the same file. Additionally, templates can help by organizing our html code in a way that it can be reused easily. Another potential benefit is that you can change the view layer of the application without even touching the javascript code or you can even hire a designer to design how the website looks like.

Now let's create a directory called views where all the templates will live.

mkdir views

In that folder we will create a file called index.hbs and it will look like as below:

<html>
    <head>
        <title>{{title}}</title>
    </head>
    <body>
        <h1>Logged in as, {{username}} </h1>
    </body>
</html>

With this we decoupled the view layer from the logic layer. This looks similar to what we had before. The only difference is the {{title}} and {{username}}. These are placeholders and the actual data will come from our express app. If we would like to use handlebars terminology, this is called html escaping. When express renders this template the resulting html code will look like this.

<html>
    <head>
        <title>PicoBlog</title>
    </head>
    <body>
        <h1>Logged in as, conpiy </h1>  
    </body>
</html>

In order to make the app work we need to configure a couple of things. First, we need to install a new npm package called hbs. To do that we have to execute in our terminal npm i hbs ( npm i is the same as npm install). This will update the package.json file to include hbs as a dependency. Hbs is a shorthand for handlebars which is the templating engine that we will use. In order for express to understand which view engine to use we have to set it up. As shown below in line 5, this is exactly what we are doing and it's the only configuration that we had to add in order for handlebars to work. Quite easy I would say!

Let's see how the code looks like now.

const express = require('express');
const app = express();

// view engine setup
app.set('view engine', 'hbs');

user = {
    username: 'conpiy'
}

app.get('/', (req, res) => res.render('index', { title: 'PicoBlog', username: user.username }));

const port = 3000;
app.listen(port, () => console.log(`Server listening at: ${port}!`));

This looks much simpler and cleaner compared to what we had before.

Conditionals

Conditional statements in our html code can help us have logic depending on a specific value that our app will define in code. The result of this is that we can render specific parts of the html page instead of everything. One example would be that I want to display a login button if the user is not logged in or the user details when the user is logged in. This can be made possible by using conditionals.

{{#if isUserLoggedIn}}
    {{username}}
{{else}}
    Login
{{/if}}

Another use case of conditionals is when we don't have a defined value for a variable then we can have a default value displayed to the user. We can have helper methods that are able to do more complex stuff than that but I am going to explain that later.

Each

With the each helper we can iterate over a list of values or objects. For this specific web application we would like to see a list of posts. As we have done before we can mock the posts list until we create an actual implementation.

const express = require('express');
const app = express();

// view engine setup
app.set('view engine', 'hbs');

user = {
    username: 'conpiy'
}

posts = [
    {
        'author': { 'username': 'alex' },
        'content': `The PicoCoder's Node.js tutorial is cool.`
    },
    {
        'author': { 'username': 'joanna' },
        'content': 'I love javascript!'
    }
]

app.get('/', (req, res) => res.render('index', { title: 'PicoBlog', username: user.username, posts }));

const port = 3000;
app.listen(port, () => console.log(`Server listening at: ${port}!`));

<html>

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

<body>
    <h1>Logged in as, {{username}} </h1>
    {{#each posts}}
    <p>{{this.content}} was written by: {{this.author.username}}</p>
    {{else}}
    <p class="empty">No content</p>
    {{/each}}

</body>

</html>

As we can see in the resulting code, we can represent the posts as a list of objects that contain the author who wrote the article and the content of the blog post. When we are in the point that we will implement this functionality for real if we structure the code and the templates in a way that they can be reused then the changes will just be the list of blog posts. Everything else will probably stay the same. This is a really nice way to solve smaller problems instead of trying to do everything in one go.

Similar to the if statements that we had earlier, the build-in handlebars helper each has an else statement which means that, if the list of posts is empty then just show "No Content". Also, to reference each item in the list of objects that we have (e.g each post in this case) we can use the word this and access the object, properties or nested properties as we do in the javascript code.

The updated website looks like this:

Partials

In the past, building websites using html, you had to build the same features in each individual page. For example, imagine this blog. It has a home page, it has individual pages for each post etc. The navigation bar and the footer are staying the same in every page. Now think what a waste would be to implement the same navigation bar in more than one pages. Partials are to the rescue! We can create smaller components that can be reused in many different pages. This is a scalable approach, to building websites with increased complexity in less time, which is amazing!

The basic partials allows you to just import in each page the partials that you want.

To use a partial, you will have to register it via hbs.registerPartials(__dirname + '/views/partials') . Then to use it in an hbs template you can do {{> navigation }}. Now, what I would like to do is to create a navigation bar component that can be reused throughout the application. To do that, we want to create a new directory under the views directory called partials. So we can do mkdir views/partials. In the partials folder create a new file called navigation.hbs. Copy and paste the following html for our navigation bar.

<ul>
    <li><a href="#home">Home</a></li>
    <li><a href="#contact">Contact</a></li>
    <li><a href="#about">About</a></li>
</ul>

The final code it will look like this

const express = require('express');
const hbs = require('hbs');
const app = express();

// view engine setup
app.set('view engine', 'hbs');

user = {
    username: 'conpiy'
}

posts = [
    {
        'author': { 'username': 'alex' },
        'content': `The PicoCoder's Node.js tutorial is cool.`
    },
    {
        'author': { 'username': 'joanna' },
        'content': 'I love javascript!'
    }
]

hbs.registerPartials(__dirname + '/views/partials');

app.get('/', (req, res) => res.render('index', { title: 'PicoBlog', username: user.username, posts }));

const port = 3000;
app.listen(port, () => console.log(`Server listening at: ${port}!`));

and the project structure will be:

microblog/
	node_modules/
	views/
		partials/
			navigation.hbs
		index.hbs
	app.js
	package.json
	package-lock.json
Chapter 2 - Navigation bar using partials

I know, it might not look pretty right now but you learn about an important concept in this chapter. In the future we will make this look awesome with the use of some css.

You can find the final result of this chapter in Github.

If you liked the tutorial, please consider subscribing to my blog. That way I get to know that my work is valuable to you and also notify you for future tutorials. Stay tuned!