Templating with HandleBars

CSCI-UA.0480-008

Templating

What's a templating engine? Describe what it does…

A templating engine is software/an application/a library that:

  • merges one or more templates (a document with placeholders)
  • … with data
  • … to create a single complete document
  • templating engines are usually built so that they are decoupled from the rest of the application that is using it

Templating Continued

In our case, we're using a templating library called handlebars:

  • …so that we could dynamically generate web pages
  • …by combining variables/data with html layouts and templates.


Why bother using templating? We were able to emit html directly by using res.end("<html>...") or res.send("<html>...")?

Why Use Templating

Using a templating engine that's decoupled from your application's logic is useful because:

  • constructing html manually is easy for small pages, but quickly gets complicated as more markup gets written
  • it can be much more difficult to spot malformed html!
  • changes to your applications logic can be tangled with changes to your presentation (and vice versa), having some separation gives you at least a chance of avoiding unwanted side-effects
  • having separate templates allows isolation of work… a designer or front-end developer can work on the templates, while a backend developer can work on the application logic
  • most templating engines are featureful… providing conveniences such as automatic character escaping (why is this important? Let's check out a bad form.)

Installation and Setup

Install:


npm install hbs --save

Configure using app.set (see other configurations you can set)


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

hbs directory structure


views/layout.hbs
views/index.hbs

Context Objects

When a template is rendered using res.render, there are two arguments.

The view or template to render… and the context object.

The context object's properties are available to your template as variable names!


// the second argument is an object
res.render('myview', {'item':'pizza', 'description':'tasty'});

{{ description }} {{ item }}


tasty pizza

Ok… Variables Make Sense

Just use double curly braces (no spaces) to drop a value in your context into your template!

But what about some logic. What if the value that I'm passing in is an array or object?

Handlebars actually has some basic facilities for:

  • looping
  • conditionals
  • …and other structures

Block Expressions / Helpers

From the handlebars docs

Block expressions allow you to define helpers that will invoke a section of your template with a different context than the current

  • …errr … basically, that means you'll be able to add control structures to your templates, like conditionals and iteration.
  • use double curly braces, but prefix your helper with a hash… and make sure you close it at the end
  • {{#helper}}stuff{{/helper}}
  • notice that there are no spaces!

Looping Over Arrays Example

The #each helper:

  • allows you iterate over a series of items
  • within #each, you can use {{this}} or {{.}} to output the current item


In your application:


app.get('/templating-arrays', function(req, res) {
	res.render('templating-arrays', {'luckyNumbers':[42, 7, 78, 3, 5]});
});

In your view:


<ul>
{{#each luckyNumbers}}
	<li>{{this}}</li>
{{/each}}
</ul>

Arrays with Named Elements

Handlebars allows named block parameters, which allows you to give each element a name while iterating:


{{#each words as |word|}}
<p>word again: {{word}}</p>
{{/each}}

Also Works With Objects

In your application:


app.get('/templating-objects', function(req, res) {
	res.render('templating-objects', {'obj':{'topping':'mushroom', 'size':'medium'}});
});

In your view, use this to get the value:


<ul>
{{#each obj}}
	<li>{{this}}</li>
{{/each}}
</ul>

Objects Continued

Assuming this context object (cat is an object):


{cat: {name:'bill furry', lives:9}};

If you want both the key and the value, use @key and this


<ul>
{{#each cat}}
<li>{{@key}}, {{this}}</li>
{{/each}}
</ul>

Dealing with an Array of Objects

Assuming this context object (points is an Array of objects):


{points: [{x:1, y:2, z:3}, {x:21, y:34, z:55}]}

Use dot notation to access properties:


<ul>
{{#each points}}
<li>{{this}}, {{this.x}}, {{this.y}}, {{this.z}} </li>
{{/each}}
</ul>

Or just the property name!


<ul>
{{#each points}}
<li>{{x}}, {{y}}, {{z}}</li>
{{/each}}
</ul>

Conditionals

(Note - no equality operators 4 U) …


{{#if isActive}}
  Active
{{else}}
  Inactive
{{/if}}

Template Error?

By the way, if you see something like:


Expecting 'ID', 'DATA', got 'INVALID'

You probably have spaces in your {{ #helper }}.

More Haaalp Plese!

Check out the docs for more block helpers!