Skip to content

Latest commit

 

History

History
executable file
·
1585 lines (1201 loc) · 57.8 KB

chapter4.md

File metadata and controls

executable file
·
1585 lines (1201 loc) · 57.8 KB

Chapter 4

Template Engines: Pug and Handlebars

A template engine is a library or a framework that uses some rules/languages to interpret data and render views. In the case of web applications, views are HTML pages (or parts of them), but they can be JSON or XML files, or GUIs in the case of desktop programs. For those of you familiar with the model–view–controller concept, templates belong to the view.

In web apps, it's beneficial to use templates because we can generate an infinite number of pages dynamically with a single template! Another side benefit is when we need to change something; we can do it in one place only.

If we go back to the diagrams in the previous chapter (traditional vs. REST API approaches), we can deduce that templates can be compiled into HTML either server-side (traditional approach) or client-side (REST API approach). No matter which approach we take, the syntax of the libraries themselves remains intact.

In this chapter we cover the following:

  • Pug syntax and features
  • Pug standalone usage
  • Handlebars syntax
  • Handlebars standalone usage
  • Pug and Handlebars usage in Express.js
  • Project: adding Pug templates to Blog

Pug Syntax and Features

Pug is a Node.js brother of Haml, in the sense that it uses whitespace and indentation as part of its language. As with a real pugs, this Pug can either be cute and friendly or can chew your butt off if you don't know how to use it. Therefore, we need to be careful to follow the proper syntax.

You can follow the Pug syntax examples in this section online, at the official web site's demo page (https://pugjs.org/api/reference.html) or by writing standalone Node.js scripts (examples are presented in the section “Pug Standalone Usage,” which appears later in this chapter).

Tags

Any text at the beginning of a line—by default—is interpreted as an HTML tag. The main advantage of Pug is that this text renders both closing and opening tags for the HTML element, as well as the <></> symbols. Therefore, we save many keystrokes as developers writing in Pug! It's very important to type as little as possible. It will allow you not only to avoid silly typos but also to avoid having a repetitive stress injury done to your hands.

The text following a tag and a space (e.g., tag <text>) is parsed as the inner HTML (i.e., content inside the element). For example, if we have the following Pug code with h1 and p tags (elements). After the tag/element name, there's a space, then text:

body
  div
    h1 Practical Node.js
    p The only book most people will ever need.
  div
    footer &copy; Apress

The text after the first space becomes the content of those elements.The output of the template above will be <h1>, <p>, and other elements with the corresponding text inside of them:

<body>
  <div>
    <h1> Practical Node.js </h1>
    <p> The only book most people will ever need. </p>
  </div>
  <div>
    <footer> &copy; Apress </footer>
  </div>
</body>

The preceding code above is an HTML <body> element. How about some more interesting HTML elements to generate the entire web page with the <head> and other tags? Sure. You can do that too (eat that, React!). Here's an example of how to define DOCTYPE, and element attributes such as lang (for html), type (for script), and id and class for div:

doctype html
html(lang="en")
  head
    title Why JavaScript is Awesome | CodingFear: programming and human circumstances
    script(type='text/javascript').
      const a = 1
      console.log(`Some JavaScript code here and the value of a is ${a}`)
  body
    h1 Why JavaScript is Awesome
    div(id="container", class="col")
      p You are amazing
      p Get on it!
      p.
        JavaScript is fun. Almost everything 
        can be written in JavaScript. It is huge.

The output will contain attributes defined with parenthesis (key=value), such as id, class, type and lang. The output will also have JavaScript code that will be executed when the page is viewed in the browsers. The output will also have text in <p>. A dot . after the element name or parenthesis allows to define text on a new line and to use multiple lines as show in the last p element.

The # means it's an id attribute, whereas the dot in the element means a class attribute. Thus, omitting the element name like we did with the #container.col will produce <div> with the id container and class col. See for yourself:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Why JavaScript is Awesome | CodingFear: programming and human circumstances</title>
    <script type="text/javascript">
      const a = 1
      console.log(`Some JavaScript code here and the value of a is ${a}`)
    </script>
  </head>
  <body>
    <h1>Why JavaScript is Awesome</h1>
    <div class="col" id="container">
      <p>You are amazing</p>
      <p>Get on it!</p>
      <p>
        JavaScript is fun. Almost everything
        can be written in JavaScript. It is huge.
      </p>
    </div>
  </body>
</html>

Check out the code bellow without the tag/element name... nothing?! Huh. You see, when you omit the tag name like in the #contaner.col, Pug will use div, so the code below:

#container.col
  p You are amazing
  p Get on it!

becomes a <div with the id container and the class col:

<div class="col" id="container">
  <p>You are amazing</p>
  <p>Get on it!</p>
</div>

You can play with these example using the code, which is in the code/ch4/pug-example/pug-method-example.js. The code uses the pug npm modules and its render() method. For example, this is a Node file and it generates HTML:

const pug = require('pug')
const pugTemplate = `body
  div
    h1 Practical Node.js
    p The only book most people will ever need.
  div
    footer &copy; Apress`

const htmlString = pug.render(pugTemplate, {pretty: true})
console.log(htmlString)

So far, we've just outputted some pre-programmed code that is not modifiable by the application. This is static and not much fun. Most of the time we want to have some dynamism in the form of variables that will allow the application itself to modify the output, that is HTML.

Variables/Locals

Pug, Express and Node developers call the data that is passed to the Pug template local. This data is available within the template as a regular Node variable. To output the value of a local/variable, use =. Let's look at some examples to make the lesson stick.

This Pug code prints values of variables title and body using the equal = symbol:

h1= title
p= body

The variables title and body are called locals. They are the data to supply to the Pug template to generate HTML. The data comes in the form of an object. It must have properties and the properties must be the same as the names of the locals that you want to use, i.e., title and body:

{
  title: "Express.js Guide",
  body: "The Comprehensive Book on Express.js"
}

The HTML output generated from the Pug template and locals shows the values of the variables title and body:

<h1>Express.js Guide</h1>
<p>The Comprehensive Book on Express.js</p>

What about HTML element attributes such as href or class? You saw some of these, already but, let's dive deeper.

Attributes

Attributes are added by putting them into parentheses right after the tag name. They follow the tagName(name=value) format. In addition, multiple attributes need to be separated by a comma. For example, this Pug code has various attributes on div, a, and other elements:

div(id="content", class="main")
  a(href="http://expressjsguide.com", title="Express.js Guide", target="_blank") Express.js Guide
  form(action="/login")
    button(type="submit", value="save")
  div(class="hero-unit") Lean Node.js!

The preceding Pug template code above turns into the following HTML with attributes rendered inside of the HTML elements:

<div class="main" id="content"><a href="http://expressjsguide.com" title="Express.js Guide" target="_blank">Express.js Guide</a>
  <form action="/login"><button type="submit" value="save"></button></form>
  <div class="hero-unit">Lean Node.js!</div>
</div>

Yes, the <a> element is right on the same line as <div>. It's a mystery to me too.

Sometimes, the value of an attribute needs to be dynamic. It's more fun this way! In this case, just use the variable name without double quotes as the value of the attribute.

Another trick is to use the pipe, or |. It allows us to define text DOM node. In other words, the line with the pipe becomes raw text. This is useful when defining multiple lines of text.

An example uses attribute values from locals/variables and defines the <input> content text yes/no on a new line:

a(href=url, data-active=isActive)
label
  input(type="checkbox", checked=isChecked)
  |  yes / no

If the template above is provided with these locals, some of which are boolean and url is a string:

{
  url: "/logout",
  isActive: true,
  isChecked: false
}

then they both—meaning template and locals data—produce the following HTML output, which doesn't have checked (false) and has yes/no as text.

<a href="/logout" data-active="data-active"></a>
<label>
  <input type="checkbox"/> yes / no
</label>

Note that the attribute with the value false is omitted from the HTML output. However, when no value is passed, true is assumed. For example, this is a Pug template with boolean attributes checked:

input(type='radio', checked)
input(type='radio', checked=true)
input(type='radio', checked=false)

The attributes checked will be omitted when the value is false. When the value is true in Pug, then the value is "checked" in HTML. This is the resulting HTML:

<input type="radio" checked="checked"/>
<input type="radio" checked="checked"/>
<input type="radio"/>

Next we will study literals.

Literals

For convenience, we can write classes and ids right after tag names. For example, we can then apply lead and center classes to a paragraph, and create a div element with the side-bar id and pull-right class (again, the pipe signifies an inner text):

div#content
  p.lead.center
    | webapplog: where code lives
    #side-bar.pull-right
    span.contact.span4
      a(href="/contact") contact us

Note that if the tag name is omitted, div is used instead. See the <div id="side-bar" class="pull-right"></div> in the generated HTML below. This <div> was created by Pug when no element name was provided, and only a an id of side-bar:

<div id="content">
  <p class="lead center">
    webapplog: where code lives
    <div id="side-bar" class="pull-right"></div>
    <span class="contact span4">
      <a href="/contact">contact us</a>
    </span>
  </p>
</div>

Pug is all about eloquence, compactness, and convenience. <div> elements are very popular for layouts. Therefore, Pug defaults to rendering <div> when there's no element name and there is a class or an id. Nice!

Our next feature is rendering text.

Text

Outputting raw text is done via |. For example, this template produces one <div> with inner text:

div
  | Pug is a template engine.
  | It can be used in Node.js and in the browser JavaScript.

If you move the | to the left, then the result will be one empty <div> with sibling text nodes.

To avoid using pipes on multiple lines, there's a dot . syntax. Thus, if you want to render all nested (indented) lines as inner text, then use dot . right after the element name. For example, this template is analogous to the preceding code in that it produces one <div> with inner text of two lines:

div.
  Pug is a template engine.
  It can be used in Node.js and in the browser JavaScript.

The result in both cases is HTML with <div> and text inside:

<div>Pug is a template engine. It can be used in Node.js and in the browser JavaScript.</div>

The dot comes in handy for writing JavaScript that executes at run time, which is the topic of the next section.

Script and Style Blocks

Sometimes, developers want to write chunks of content for script or style tags in the HTML! This is possible with a dot.

For example, we can write inline front-end JavaScript like this:

script.
  console.log('Hello Pug!')
  setTimeout(function(){
    window.location.href='http://rpjs.co'
  },200))
  console.log('Good bye!')

And the HTML output will have the <script> tag with all of our code:

<script>
  console.log('Hello Pug!')
  setTimeout(function() {
  window.location.href = 'http://rpjs.co'
  }, 200))
  console.log('Good bye!')
</script>

Did you like this little trick with the dot and JavaScript? Of course! But this code is not executed until the page loads. In other words, it's runtime but not compile.

JavaScript Code

Contrary to the previous example, if we want to use any JavaScript at template compilation time—in other words, to write executable JavaScript code that manipulates the output of the Pug (i.e., HTML)—we can use the -, =, or != symbols. This may come in handy when we output HTML elements and inject JavaScript.

Obviously, these types of things should be done carefully to avoid cross-site scripting (XSS) attacks. For example, if we want to define an array and output <> symbols, we can use !=.

- var arr = ['<a>','<b>','<c>']
ul
  - for (var i = 0; i< arr.length; i++)
    li
      span= i
      span!="unescaped: " + arr[i] + " vs. "
      span= "escaped: " + arr[i]

The Pug above produces the following HTML which does NOT include JavaScript but the result of the JavaScript code, because this JS is a compile-time JS for Pug. This is not run-time JS for a browser as was defined with script. earlier. The resulting HTML has <ul> and three <li> elements:

<ul>
  <li><span>0</span><span>unescaped: <a> vs. </span><span>escaped: &lt;a&gt;</span></li>
  <li><span>1</span><span>unescaped: <b> vs. </span><span>escaped: &lt;b&gt; </span></li>
  <li><span>2</span><span>unescaped: <c> vs. </span><span>escaped: &lt;c&gt; </span></li>
</ul>

Tip One of the main differences between Pug and Handlebars is that the former allows pretty much any JavaScript in its code, whereas the latter restricts programmers to only a handful of built-in and custom-registered helpers.

Comments

When it comes to comments, we have a choice to render/output them into HTML or not. To render/output them into HTML, use JavaScript style //; to not render them, use //-. For example, here are two comments:

// content goes here
p Node.js is a non-blocking I/O for scalable apps.
//- @todo change this to a class
p(id="footer") Copyright 2014 Azat

The Pug above with comments outputs the HTML style comments with // but hides them with //-. Thus, the resulting HTML has only content goes here without @todo change this to a class:

<!-- content goes here-->
<p>Node.js is a non-blocking I/O for scalable apps.</p>
<p id="footer">Copyright 2014 Azat</p>

Of course, views (i.e., templates) benefit greatly from an if/else condition. Let's cover them next.

Conditions (if)

Interestingly enough, in addition to the standard JavaScript code, where the if statement can be used by prefixing it with -, we can use an even shorter Pug alternative with no prefix and no parentheses. For example, this if/else works fine:

- var user = {}
- user.admin = Math.random()>0.5
if user.admin
    button(class="launch") Launch Spacecraft
else
    button(class="login") Log in

There's also unless, which is equivalent to not or !.

Iterations (each loops)

Similar to conditions, iterators in Pug can be written simply with each. For example, this is code to iterate over an array of programming languages and create paragraphs for each of them:

- var languages = ['php', 'node', 'ruby']
div
  each value, index in languages
    p= index + ". " + value

The HTML output with three <p> elements is as follows:

<div>
  <p>0. php</p>
  <p>1. node</p>
  <p>2. ruby</p>
</div>

The same iterative each construction works with objects as well. Developers even can access a key value. Take a look at this object with languages as keys and their importance as values:

- var languages = {'php': -1, 'node': 2, 'ruby':1}
div
  each value, key in languages
    p= key + ": " + value

The Pug above is compiled into the HTML output in which each iteration over the array values produces a paragraph <p> element for each language:

<div>
  <p>php: -1</p>
  <p>node: 2</p>
  <p>ruby: 1</p>
</div>

Next are filters!

Filters

Filters are used when there are blocks of texts written in a different language. For example, the filter for Markdown looks like this:

p
  :markdown
    # Practical Node.js

Note The Markdown modules still need to be installed. The marked and markdown npm packages are often used for this. There's no need for an additional configuration; just install them in the project's local node_modules folder.

Interpolation

Interpolation is mixing of strings and dynamic values from variables. That's another term that will make you look at least five (5) IQ points smarter. You are welcome.

In Pug, interpolation is achieved via the syntax with curly braces and a hashtag: #{name}, where name is the name of a variable. For example, to output title in a paragraph, simply use #{title} in the text, as in the following code:

- var title = "React Quickly: Painless web apps with React, JSX, Redux, and GraphQL"
p Read the #{title} in PDF, MOBI and EPUB

The interpolation is processed at the template compilation. Therefore, don't use interpolation in executable JavaScript, that is, JS with -. For the - JS, use standard ES6 string interpolation with ${name}.

Case

Case allows Node developers to avoid a chain of if/else conditions. You probably used something similar. In other languages, case implemented with switch. Here's an example of the case statement in Pug:

- var coins = Math.round(Math.random()*10)
case coins
  when 0
    p You have no money
  when 1
    p You have a coin
  default
    p You have #{coins} coins!

Mixins

Mixins are functions that take parameters and produce some HTML. They are super cool because they allow you reuse boatloads of code if used correctly. The declaration syntax is mixin name(param, param2,...), and the usage is +name(data). For example, here I define row and table mixins, which I use later with real data from arrays:

mixin row(items)
  tr
    each item, index in items
      td= item

mixin table(tableData)
  table
    each row, index in tableData
      +row(row)

- var node = [{name: "express"}, {name: "hapi"}, {name: "derby"}]
+table(node)
- var js = [{name: "backbone"}, {name: "angular"}, {name: "ember"}]
+table(js)

The preceding Pug code, above when used in Express or elsewhere, produces the following output by "invoking" the mixins table and row just as a function would be invoked with arguments (bonus: developers can use table and row mixins over and over for other data!):

<table>
  <tr>
    <td>express</td>
  </tr>
  <tr>
    <td>hapi</td>
  </tr>
  <tr>
    <td>derby</td>
  </tr>
</table>
<table>
  <tr>
    <td>backbone</td>
  </tr>
  <tr>
    <td>angular</td>
  </tr>
  <tr>
    <td>ember</td>
  </tr>
</table>

Include

include is a way to split logic into a separate file for the purpose of reusing it across multiple files. Don't confuse this with ES6 include. That's JavaScript, but we are talking about Pug here.

This include is a top-to-bottom approach, meaning we dictate what to use in the file that includes another file. The file that includes is processed first (we can define locals there), and then the included file is processed (we can use earlier defined locals).

To include a Pug template, use include /path/filename. No need for double quotes " or single quotes '. I like it! For example, in a layout file you can import a header:

include ./includes/header

Notice there's no need for double or single quotes for the template name and its path. And it's possible to traverse up the folder tree. This footer can be in a parent folder's includes folder:

include ../includes/footer

But, there's no way to use a dynamic value for the file and path (use a variable), because includes/partials are handled at compilation (not at runtime).

Extend

extend is a bottom-to-top approach (as oppose to include), in the sense that the included file commands which parts of the main file it wants to replace. The way it works is with extend filename and block blockname statements.

In file_a, which is like a layout you define blocks, define block elements with some default content:

block header
  p some default text
block content
  p Loading ...
block footer
  p copyright

In file_b, which is like a subview, you define what layout to use and what blocks to overwrite (and what not to, by omission).

For example, in this file_b file, the header and content blocks will have new content, but footer will stay as in file_a. Here's the file_b example:

extend file_a
block header
  p very specific text
block content
  .main-content

The bottom line is that extend and block implement inverted inheritance pattern.

Standalone Pug Usage

Template engines (Pug) and web frameworks (Express) go together like ketchup and hotdogs—but not always. Template engines are not not always used with Node.js frameworks like Express.js. Sometimes, we might just want to use Pug in a standalone manner. The use cases include generating an email template, precompiling Pug before deployment, and debugging. In this section, we do the following:

  • Install a Pug module
  • Create our first Pug file
  • Create a Node.js program that uses the Pug file
  • Compare pug.compile, pug.render, and pug.renderFile

To add a pug dependency to your project, or if you're starting from scratch from an empty project folder, do the following:

  1. Create a package.json file manually or with $ npm init -y.
  2. Install and add pug to package.json with $ npm i pug –save. See the results in Figure 4-1.
  3. Create a Node file.
  4. Import pug in the Node file.
  5. Invoke a method from pug module in your Node file.

alt

Figure 4-1. Installing Pug

Tip Add {pretty: true} to pug.render(), as in pug.render(pugTemplate, {pretty: true}), in order to have properly formatted, pretty HTML.

Let's say we have some Node.js script that sends an email and we need to use a template to generate HTML dynamically for the email. This is how it might look (file pug-example.pug):

.header
  h1= title
  p
.body
  p= body
.footer
  div= By
    a(href="http://twitter.com/#{author.twitter}")= author.name
  ul
    each tag, index in tags
      li= tag

In this case, our Node.js script needs to hydrate, or populate, this template with the following data:

  • title: String
  • tags: Array
  • body: String
  • author: String

We can extract these variables from multiple sources (databases, file systems, user input, tassology, and so on). For example, in the pug-example.js file, we use hard-coded values for title, author, tags, but pass through a command-line argument for body using process.argv[2]:

const pug = require('pug'),
  fs = require('fs')

let data = {
  title: 'Practical Node.js',
  author: {
    twitter: '@azatmardan',
    name: 'Azat'
  },
  tags: ['express', 'node', 'javascript']
}
data.body = process.argv[2]

fs.readFile('pug-example.pug', 'utf-8', (error, source) => {
  let template = pug.compile(source)
  let html = template(data)
  console.log(html)
})

In this way, when we run $ node pug-example.js 'email body', we get the HTML output printed in the terminal as shown in Figure 4-2.

alt

Figure 4-2. The result of pug-example output

The "prettified" HTML output with proper spaces and indentation that I took from the terminal looks as follows:

<div class="header">
    <h1>Practical Node.js</h1>
    <p></p>
</div>
<div class="body">
    <p>email body</p>
</div>
<div class="footer">
    <div><a href="http://twitter.com/@azatmardan">Azat</a>
    </div>
    <ul>
        <li>express</li>
        <li>node</li>
        <li>javascript</li>
    </ul>
</div>

In addition to pug.compile(), the Pug API has the functions pug.render() and pug.renderFile(). For example, the previous file can be rewritten with pug.render():

fs.readFile('pug-example.pug', 'utf-8', (error, source) => {
  const html = pug.render(source, data)
  console.log(html)
})

Furthermore, with pug.renderFile(), the pug-example.js file is even more compact because it will do two things at the same time: read a file and render it:

pug.renderFile('pug-example.pug', data, (error, html) => {
  console.log(html)
})

Note Pug can also be used as a command-line tool after installing it with the -g or --global option via npm. For more information, run pug -h or see the official documentation (http://pug-lang.com/command-line).

To use Pug in a browser, you can use browserify(https://github.com/substack/node-browserify) and its pugify (https://www.npmjs.org/package/pug-browser) middleware.

Note To use the same Pug templates on front-end (browser) and server sides, I recommend jade-browser (https://www.npmjs.org/package/jade-browser) by Storify, for which I was the maintainer for a time during my work there. jade-browser acts as an Express.js middleware, and exposes server-side templates to the browser along with helpful utility functions.

Handlebars Syntax

The Handlebars library is another template engine. It inherits from Mustache and, for the most part, is compatible with Mustache's syntax. However, Handlebars adds more features. In other words, Handlebars is a superset of Mustache.

Unlike Pug, Handlebars by design was made so that developers can't write a lot of JavaScript logic inside the templates. This helps to keep templates lean and related strictly to the representation of the data (no business logic).

Another drastic difference between Pug and Handlebars is that the latter requires full HTML code (<, >, closing </> tags, and so on). For this reason it could care less about whitespace and indentation, which means that it's easy to copypasta your existing HTML and make it Handlebars, and that developers have to type more code when writing templates from scratch.

Variables

A Handlebars expression is {{, some content, followed by }}, hence the name of the library (see the resemblance to handlebars on a bicycle?). For example, this Handlebars code:

<h1>{{title}}</h1>
<p>{{body}}</p>

with data that has title and body properties:

{
  title: "Express.js Guide",
  body: "The Comprehensive Book on Express.js"
}

renders the elements with values from title and body:

<h1>Express.js Guide</h1>
<p>The Comprehensive Book on Express.js</p>

Iteration (each)

In Handlebars, each is one of the built-in helpers; it allows you to iterate through objects and arrays. Inside the block, we can use @key for the former (objects), and @index for the later (arrays). In addition, each item is referred to as this. When an item is an object itself, this can be omitted, and just the property name is used to reference the value of that property.

The following are examples of the each helper block in Handlebars:

<div>
{{#each languages}}
  <p>{{@index}}. {{this}}</p>
{{/each}}
</div>

The template above is supplied with this data that has array of strings:

{languages: ['php', 'node', 'ruby']}

and output this HTML upon compilation, which has <p> for each array element:

<div>
  <p>0. php</p>
  <p>1. node</p>
  <p>2. ruby</p>
</div>

Unescaped Output

By default, Handlebars escapes values. If you don't want Handlebars to escape a value, use triple curly braces: {{{ and }}}.

As data, let's use this object that has an array with some HTML tags (angle braces):

{
  arr: [
    '<a>a</a>',
    '<i>italic</i>',
    '<strong>bold</strong>'
  ]
}

To apply this Handlebars template to our data above (i.e., hydration) use an iterator each with {{{this}}} for the unescaped value of an individual array item, which is HTML and hence needs to be unescaped:

<ul>
    {{#each arr}}
    <li>
      <span>{{@index}}</span>
      <span>unescaped: {{{this}}} vs. </span>
      <span>escaped: {{this}}</span>
    </li>
  {{/each}}
</ul>

The hydrated template produces the following HTML by printing array indices ({{@index}}), unescaped HTML ({{{this}}}) and escaped HTML ({{this):

<ul>
  <li>
    <span>0</span>
    <span>unescaped: <a>a</a> vs. </span>
    <span>escaped: &lt;a&amp;gt;a&lt;/a&gt;</span>
  </li>
  <li>
    <span>1</span>
    <span>unescaped: <i>italic</i> vs. </span>
    <span>escaped: &lt;i&gt;italic&lt;/i&gt;</span>
  </li>
  <li>
    <span>2</span>
    <span>unescaped: <strong>bold</strong> vs. </span>
    <span>escaped: &lt;strong&gt;bold&lt;/strong&gt;</span>
  </li>
</ul>

Conditions (if)

if is another built-in helper invoked via #. For example, this Handlebars code uses an if/else condition to check for a user.admin value (if a user is an administrator):

{{#if user.admin}}
  <button class="launch"> Launch Spacecraft</button>
{{else}}
  <button class="login"> Log in</button>
{{/if}}

The template is populated with data that will make the if/else condition true:

{
  user: {
    admin: true
  }
}

Everything turns into this HTML output, which has a launch element rendered due to the value of user.admin being true:

<button class="launch">Launch Spacecraft</button>

Unless

To inverse an if not ... (if ! ...) statement (convert negative to positive), we can harness the unless built-in helper block. For example, the previous code snippet can be rewritten with unless.

The Handlebars code that checks the truthiness of the admin flag (property user.admin). If the value is true, then else will be applied. Notice the change in Log in and Launch Spacecraft. They are flipped now compared to if/else:

{{#unless user.admin}}
  <button class="login"> Log in</button>
{{else}}
  <button class="launch">Launch Spacecraft</button>
{{/unless}}

We supply our template with the data that makes the user an administrator:

{
  user: {
    admin: true
  }
}

The HTML output renders the launch button, which is available only to admins because this button was in else, we used unless, and the value is true.

<button class="launch">Launch Spacecraft</button>

With

In case there's an object with nested properties, and there are a lot of them, it's possible to use with to pass the context.

We have this Handlebars code that is handling a user's contact and address information:

{{#with user}}
  <p>{{name}}</p>
  {{#with contact}}
    <span>Twitter: @{{twitter}}</span>
  {{/with}}
  <span>Address: {{address.city}},
{{/with}}
{{user.address.state}}</span>

Then we merge the template with this data. Notice the properties' names are the same as in the Handlebars template, there's only one reference to the user object:

{user: {
  contact: {
    email: 'hi@azat.co',
    twitter: 'azat_co'
  },
  address: {
    city: 'San Francisco',
    state: 'California'
  },
  name: 'Azat'
}}

The snippets above, when compiled, produce HTML that prints values using the object name for every property:

<p>Azat</p>
<span>Twitter: @azatmardan</span>
<span>Address: San Francisco, California
</span>

Comments

To output comments, use regular HTML <!-- and -->. To hide comments in the final output, use {{! and }} or {{!-- and --}}. For example, the following code below has two types of comments:

<!-- content goes here -->
<p>Node.js is a non-blocking I/O for scalable apps.</p>
{{! @todo change this to a class}}
{{!-- add the example on {{#if}} --}}
<p id="footer">Copyright 2018 Azat</p>

The preceding code outputs the comments with <!-- ... --> but omits comments with {{! ... }} so the result is this:

<!-- content goes here -->
<p>Node.js is a non-blocking I/O for scalable apps.</p>
<p id="footer">Copyright 2014 Azat</p>

Custom Helpers

Custom Handlebars helpers are similar to built-in helper blocks and Pug mixins. To use custom helpers, we need to create them as a JavaScript function and register them with the Handlebars instance.

For example, let's assume we have a custom helper table which we'll register (i.e., define) later in the JavaScript/Node.js code, then this Handlebars template uses our table:

{{table node}}

Here goes the JavaScript/Node.js that registers or tells the Handlebars compiler what to do when it encounters the custom table function (i.e., print an HTML table out of the provided array):

handlebars.registerHelper('table', (data) => {
  let str = '<table>'
  for (let i = 0; i < data.length; i++ ) {
    str += '<tr>'
    for (var key in data[i]) {
      str += '<td>' + data[i][key] + '</td>'
    }
    str += '</tr>'
  }
  str += '</table>'
  return new handlebars.SafeString (str)
})

The following is our array for the table data. It has an array of object. Each object has name and URL:

[
  {name: 'express', url: 'http://expressjs.com/'},
  {name: 'hapi', url: 'http://spumko.github.io/'},
  {name: 'compound', url: 'http://compoundjs.com/'},
  {name: 'derby', url: 'http://derbyjs.com/'}
]

The resulting HTML from iterating over the name and URL objects within the table function looks like this:

<table>
    <tr>
        <td>express</td>
        <td>http://expressjs.com/</td>
    </tr>
    <tr>
        <td>hapi</td>
        <td>http://spumko.github.io/</td>
    </tr>
    <tr>
        <td>compound</td>
        <td>http://compoundjs.com/</td>
    </tr>
    <tr>
        <td>derby</td>
        <td>http://derbyjs.com/</td>
    </tr>
</table>

Thus, helpers are good for reusing the code. Another way to reuse code is includes or partials.

Includes (Partials)

In Handlebars, includes or partials templates are interpreted by the {{> partial_name}} expression. Partials are akin to helpers and are registered with Handlebars.registerPartial(name, source), where name is a string and source is a Handlebars template code for the partial (JS/Node code, not template):

Handlebars.registerPartial('myPartial', '{{name}}')

Calling the partial is done with the following syntax (written in the Handlebars template, not JS/Node code):

{{> myPartial }}

For more on includes and partials, see the documentation at http://handlebarsjs.com/partials.html.

Standalone Handlebars Usage

Developers can install Handlebars via npm with $ npm install handlebars or $ npm install handlebars --save, assuming either node_modules or package.json is in the current working directory (see the results of a sample installation in Figure 4-3).

alt

Figure 4-3. Installing Handlebars

Note Handlebars can be installed via npm as a command-line tool with the -g or --global options. For more information on how to use Handlebars in this mode, refer to the $ handlebar command or the official documentation (https://github.com/wycats/handlebars.js/#usage-1).

Here's an example of standalone Node.js Handlebars usage from handlebars-example.js in which we import modules, then define data object (with book info), and then register a few helpers and generate HTML:

const handlebars = require('handlebars')
const fs = require('fs')
const path = require('path')

const data = {
  title: 'practical node.js',
  author: '@azatmardan',
  tags: ['express', 'node', 'javascript']
}

data.body = process.argv[2]
const filePath = path.join(__dirname,
  'handlebars-example.html')
  
data.tableData = [
  {name: 'express', url: 'http://expressjs.com/'},
  {name: 'hapi', url: 'http://spumko.github.io/'},
  {name: 'compound', url: 'http://compoundjs.com/'},
  {name: 'derby', url: 'http://derbyjs.com/'}
]

fs.readFile(filePath, 'utf-8', (error, source) => {
  if (error) return console.error(error)
  // Register helper to generate table HTML from data (array)
  handlebars.registerHelper('table', (data) => {
    let str = '<table>'
    for (let i = 0; i < data.length; i++) {
      str += '<tr>'
      for (var key in data[i]) {
        str += '<td>' + data[i][key] + '</td>'
      }
      str += '</tr>'
    }
    str += '</table>'
    return new handlebars.SafeString(str)
  })
  // Register helper to create capitalize a string
  handlebars.registerHelper('custom_title', (title) => {
    let words = title.split(' ')
    for (let i = 0; i < words.length; i++) {
      if (words[i].length > 4) {
        words[i] = words[i][0].toUpperCase() + words[i].substr(1)
      }
    }
    title = words.join(' ')
    return title
  })

  // Compile the template and hydrate it with data to generate HTML
  const template = handlebars.compile(source)
  const html = template(data)
  console.log(html)
})

And the handlebars-example.html template file that uses custom_title helper has the following content that calls the helper and outputs some other properties:

<div class="header">
    <h1>{{custom_title title}}</h1>
</div>
<div class="body">
    <p>{{body}}</p>
</div>
<div class="footer">
    <div><a href="http://twitter.com/{{author.twitter}}">{{autor.name}}</a>
    </div>
    <ul>
      {{#each tags}}
        <li>{{this}}</li>
      {{/each}}
    </ul>
</div>

To produce this HTML when we run $ node handlebars-example.js 'email body', use the following:

<div class="header">
    <h1>Practical Node.js</h1>
</div>
<div class="body">
    <p>email body</p>
</div>
<div class="footer">
    <div><a href="http://twitter.com/"></a>
    </div>
    <ul>
        <li>express</li>
        <li>node</li>
        <li>javascript</li>
    </ul>
</div>

To use Handlebars in the browser, download the library in a straightforward manner from the official web site (http://handlebarsjs.com) and include it in your pages. Alternatively, it's possible to use just the runtime version from the same web site (which is lighter in size) with precompiled templates. Templates can be precompiled with the Handlebars command-line tool.

Pug and Handlebars Usage in Express.js

By default, Express.js uses either a template extension provided to the response.render (or res.render) method or the default extension set by the view engine setting, to invoke the require and __express methods on the template library. In other words, for Express.js to utilize a template engine library out of the box, that library needs to have the __express method.

When the template engine library doesn't provide the __express method, or a similar one with (path, options, callback) parameters, it's recommended that you use Consolidate.js (https://github.com/visionmedia/consolidate.js/).

Let's look at a quick example of an abstraction library for templates called Consolidate.js. In this example, I use the template engine Swig. I picked this template engine because most likely you never heard of it and this makes it a good illustration for an abstraction library like Consolidate. So Swig comes from the consolidate module. I connected it to express with the app.engine('html', cons.swig) statement. See the full server implementation that renders Swig templates:

const express = require('express')
const cons = require('consolidate')
const path = require('path')

let app = express()

app.engine('html', cons.swig)

app.set('view engine', 'html')
app.set('views', path.join(__dirname, 'templates'))

var platforms = [
  { name: 'node' },
  { name: 'ruby' },
  { name: 'python' }
]

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Consolidate This'
  })
})

app.get('/platforms', (req, res) => {
  res.render('platforms', {
    title: 'Platforms',
    platforms: platforms
  })
})

app.listen(3000, () => {
  console.log('Express server listening on port 3000')
})

As usual, the source code is in the GitHub repository, and the snippet is in the code/ch4/consolidate folder.

For more information on how to configure Express.js settings and use Consolidate.js, refer to the still-up-to-date book on Express.js version 4—Pro Express.js (Apress, 2014), which is available on all major book stores, and of course at https://amzn.to/2tlSwNw.

Pug and Express.js

Pug is compatible with Express.js out of the box (in fact, it's the default choice), so to use Pug with Express.js, you just need to install a template engine module (pug) (https://www.npmjs.org/package/pug) and provide an extension to Express.js via the view engine setting.

For example, in the main Express server file we set the view engine setting as pug to let Express know which library to use for templates:

app.set('view engine', 'pug')

Of course, developers need to install the pug npm module into their project so the pug package is stored locally in node_modules. Express will use the name pug provided to view engine to import the pug package and also use the pug as a template files extension in the views folder (views is the default name).

Note If you use the $ express <app_name> command-line tool, you can add the option for engine support, i.e., the –e option for EJS and –H for Hogan. This will add EJS or Hogan automatically to your new project. Without either of these options, the express-generator (versions 4.0.0-4.2.0) will use Pug.

In the route file, we can call the template—for example, views/page.pug (the views folder name is another Express.js default, which can be overwritten with the view setting):

app.get('/page', (req, res, next) => {
  //get the data dynamically
  res.render('page', data)
})

If we don't specify the view engine setting, then the extension must be passed explicitly to res.render() as a first argument, such as:

  res.render('page.pug', data)

Next, let's cover the Express usage for Handlebars.

Handlebars and Express.js

Contrary to Pug, the Handlebars library from http://handlebarsjs.com doesn't come with the __express method, but there are a few options to make Handlebars work with Express.js:).

Here's how we can use the hbs approach (extension hbs). Somewhere in the configuration section of the main Express file (file that we launch with the $ node command), write the following statements:

// Imports
app.set('view engine', 'hbs')
// Middleware

Or, if another extension is preferable, such as html, we see the following:

app.set('view engine', 'html')
pp.engine('html', require('hbs').__express)

The express-handlebars approach usage is as follows:

const exphbs  = require('express-handlebars')
app.engine('handlebars', exphbs({defaultLayout: 'main'}))
app.set('view engine', 'handlebars')

Good. Now we can put our knowledge to practice.

Project: Adding Pug Templates to Blog

Lastly, we can continue with Blog. In this section we add main pages using Pug, plus we add a layout and some partials:

  • layout.pug: Global app-wide template
  • index.pug: Home page with the list of posts
  • article.pug: Individual article page
  • login.pug: Page with a login form
  • post.pug: Page for adding a new article
  • admin.pug: Page to administer articles after logging in

Because the templates in this mini-project require data, we'll skip the demo until Chapter 5, where we'll plug in the MongoDB database. So the source code for the Pug templates is exactly the same as in the code/ch5 folder of the GitHub repository azat-co/practicalnode: https://github.com/azat-co/practicalnode. Feel free to copy it from there or follow the instructions to implement listed below in this section.

layout.pug

Let's open the project where we left off in the previous chapter and add layout.pug with the document type statement:

doctype html

Now we can add the main tags of the page:

html
  head

The title of the each page is provided from the appTitle variable (a.k.a., local):

    title= appTitle

Then, in the head tag, we list all the front-end assets that we need app-wide (on each page):

    script(type="text/javascript", src="js/jquery-2.0.3.min.js")
    link(rel="stylesheet", href="/css/bootstrap-3.0.2/css/bootstrap.min.css")
    link(rel="stylesheet", href="/css/bootstrap-3.0.2/css/bootstrap-theme.min.css")
    link(rel="stylesheet", href="/css/style.css")
    script(type="text/javascript", src="/css/bootstrap-3.0.2/js/bootstrap.min.js")
    script(type="text/javascript", src="/js/blog.js")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")

The main content lives in body, which has the same level indentation as head:

  body

Inside the body, we write an id and some classes for the styles that we'll add later:

    #wrap
      .container

The appTitle value is printed dynamically, but the p.lead element only has text:

        h1.page-header= appTitle
        p.lead Welcome to example from Express.js Experience by
          a(href="http://twitter.com/azat_co") @azatmardan
          |. Please enjoy.

The block sections can be overwritten by the children templates (templates that extend this file):

        block page
        block header
          div

Menu is a partial (i.e., an include) that is stored in the views/includes folder. Note the absence of quotation marks:

            include includes/menu

In this block named alert, we can display messages for users, so let's use special alerty classes on a div (the indentation is preserved to show hierarchy):

            block alert
              div.alert.alert-warning.hidden

Main content goes in this block. It is empty now because other template will define it:

        .content
          block content

Lastly, the footer block with div with the container class and with p with text and a link (link is wrapped in text) looks as follows:

    block footer
      footer
        .container
          p
            | Copyright &copy; 2018 | Issues? Submit to
            a(href="https://github.com/azat-co/blog-express/issues") GitHub
            | .

To give you a full picture as well as preserve proper indentation (which is PARAMOUNT in Pug), the full code of layout.pug is as follows:

doctype html
html
  head
    title= appTitle
    script(type="text/javascript", src="js/jquery-2.0.3.min.js")
    link(rel="stylesheet", href="/css/bootstrap-3.0.2/css/bootstrap.min.css")
    link(rel="stylesheet", href="/css/bootstrap-3.0.2/css/bootstrap-theme.min.css")
    link(rel="stylesheet", href="/css/style.css")
    script(type="text/javascript", src="/css/bootstrap-3.0.2/js/bootstrap.min.js")
    script(type="text/javascript", src="/js/blog.js")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
  body
    #wrap
      .container
        h1.page-header= appTitle
        p.lead Welcome to example from Express.js Experience by
          a(href="http://twitter.com/azat_co") @azatmardan
          |. Please enjoy.
        block page
        block header
          div
            include includes/menu
        block alert
          div.alert.alert-warning.hidden
        .content
          block content
    block footer
      footer
        .container
          p
            | Copyright &copy; 2014 | Issues? Submit to
            a(href="https://github.com/azat-co/blog-express/issues") GitHub
            | .

Next is the home page.

index.pug

Now, we can look at the home page template index.pug that extends layout.pug. Remember the syntax? It's extends name:

extends layout

Because we can overwrite some blocks, we set the menu variable to index, so the menu include (i.e., menu.pug) can determine which tab to show as active:

block page
  - var menu = 'index'

Of course, we need to overwrite the content block. Ergo, the main content with the list of articles that comes from locals iterates over the blog posts (articles). Each article link has a title and, needless to say, a URL that is formed by the article.slug value. When there are no posts/articles, then we show a message that nothing has been published yet. The code is as follows:

block content
  if (articles.length === 0)
    | There's no published content yet.
    a(href="/login") Log in
    |  to post and publish.
  else
    each article, index in articles
      div
        h2
          a(href="/articles/#{article.slug}")= article.title

For your reference and to show the ease of comprehension in Pug's style, the full code of index.pug is as follows. You can see extends and two block overwrites (of layout):

extends layout

block page
  - var menu = 'index'
block content
  if (articles.length === 0)
    | There's no published content yet.
    a(href="/login") Log in
    |  to post and publish.
  else
    each article, index in articles
      div
        h2
          a(href="/articles/#{article.slug}")= article.title

Figure 4-4 shows how the home page looks after adding style sheets.

alt

Figure 4-4. The home page of Blog shows menu and the titles of the published articles

Phew. Next is the page for the actual blog posts/articles.

article.pug

The individual article page (Figure 4-5) is relatively unsophisticated because most of the elements are abstracted into layout.pug. We only have extends and then overwrite the content block without the article title (h1 heading) and article's text (p for paragraph).

extends layout

block content
  p
    h1= title
    p= text

This is the awesomeness which we receive for free thanks to Twitter Bootstrap and h1 and p elements. You can clearly see that even despite defining only h1 and p, the webpage /articles/node-fundamentals has a page title menu and the footer. That's due to the inheritance, extends, and layout.pug.

alt

Figure 4-5. The article page

Did you notice that "Log in" link? Let's implement the login page next.

login.pug

Similarly to article.pug, the login page uses login.pug, which contains... not much! Only a form and a button with some minimal Twitter Bootstrap classes/markup.

So as with article.pug, we extend layout and overwrite two blocks—one for the active menu value and the other for the content, which is the main part of the page. This main part has guess what? A LOGIN FORM! This is file login.pug:

extends layout

block page
  - var menu = 'login'

block content
  .col-md-4.col-md-offset-4
    h2 Log in
    div= error
    div
      form(action="/login", method="POST")
        p
          input.form-control(name="email", type="text", placeholder="hi@azat.co")
        p
          input.form-control(name="password", type="password", placeholder="***")
        p
          button.btn.btn-lg.btn-primary.btn-block(type="submit") Log in

Again, thanks to Twitter Bootstrap, our page looks stellar. It has a menu because of extends and layout.pug. Figure 4-6 shows how the login page looks.

alt

Figure 4-6. The login page

But how to create a new article? Easy! By posting its title and text.

post.pug

The post page (Figure 4-7) has another form and it also extends layout.pug. This time, the form contains a text area element that will become the main text of the article. In addition to the article text, there are title, and the URL segment (or path) to the article which is called slug 🐌.

extends layout
block page
  - var menu = 'post'
block content

    h2 Post an Article
    div= error
    div.col-md-8
      form(action="/post", method="POST", role="form")
        div.form-group
          label(for="title") Title
          input#title.form-control(name="title", type="text", placeholder="JavaScript is good")
        div.form-group
          label(for="slug") Slug
          input#slug.form-control(name="slug", type="text", placeholder="js-good")
          span.help-block This string will be used in the URL.
        div.form-group
          label(for="text") Text
          textarea#text.form-control(rows="5", name="text", placeholder="Text")
        p
          button.btn.btn-primary(type="submit") Save

To give you some visual of the Pug of post.pug, take a look at the page for posting new articles. The action attribute of <form> will allow browsers to send the data to the backend and then Express will take care of it by processing, and our Node code will save it to the database.

alt

Figure 4-7. The post page

If a valid administrator user is logged in, then we want to show an admin interface. See the Admin link in the menu? Let's implement the admin page to which this menu link leads to.

admin.pug

The admin page (Figure 4-8) has a loop of articles just like the home page, but in addition to just showing articles, we can include a front-end script (js/admin.js) specific to this page. This script will do some AJAX-y calls to publish and unpublish articles. These functions will be available only to admins. Of course we will need an server-side validation on the backend later. Don't trust only the front-end validation or authorization!

So the admin.pug file starts with the layout extension and has content overwrite, in which there's a table of articles. In each row of the table, we use glyphicon to show a fancy icon for pause or play ▶️. The icons come from Twitter Bootstrap and are enabled via classes:

extends layout

block page
  - var menu = 'admin'

block content
  div.admin
    if (articles.length === 0 )
      p
        | Nothing to display. Add a new
        a(href="/post") article
        |.
    else
      table.table.table-stripped
        thead
          tr
            th(colspan="2") Actions
            th Post Title
        tbody
          each article, index in articles
            tr(data-id=`${article._id}`, class=(!article.published)?'unpublished':'')
              td.action
                button.btn.btn-danger.btn-sm.remove(type="button")
                  span.glyphicon.glyphicon-remove(title="Remove")
              td.action
                button.btn.btn-default.btn-sm.publish(type="button")
                  span.glyphicon(class=(article.published) ? "glyphicon-pause" : "glyphicon-play", title=(article.published) ? "Unpublish" : "Publish")
              td= article.title
      script(type="text/javascript", src="js/admin.js")

Please notice that we use ES6 string template (or interpolation) to print article ids as attributes data-id (indentation was removed):

tr(data-id=`${article._id}`, class=(!article.published) ? 'unpublished':'')

And a conditional (ternary) operator (https://github.com/donpark/hbs) is used for classes and title attributes. Remember, it's JavaScript! (Indentation has was removed for better viewing.)

span.glyphicon(class=(article.published) ? "glyphicon-pause" : "glyphicon-play", title=(article.published) ? "Unpublish" : "Publish")

The result is a beautiful admin page (Okay, enough with sarcasm and saying Twitter Bootstrap is stellar, pretty or cute. It's not... but compared to standard HTML, which puts me to sleep, Twitter Bootstrap style is a HUGE improvement.) It has functionality to publish and unpublish articles.

alt

Figure 4-8. The admin page shows the list of published and draft articles

Summary

In this chapter, you learned about the Pug and Handlebars templates (variables, iterations, condition, partials, unescaping, and so forth), and how to use them in a standalone Node.js script or within Express.js. In addition, the main pages for Blog were created using Pug.

In the next chapter, we'll learn how to extract the data from a database and save new data to it. You'll become familiar with MongoDB. Onwards.