Building Your Zippy Courses Theme Part 3: A Guide to Handlebars

Using Handlebars in Zippy Courses Themes

The first two technologies for building custom themes, HTML, and CSS were probably familiar (we bet you've at least heard of them before). HTML is for structuring your pages, and CSS is for making them pretty.

So what does Handlebars contribute to the equation?

Handlebars lets you access all of the data you need to fill your template with content and control how your template makes decisions about what to include.

In this article, we're going to walk you through the most common uses for Handlebars in Zippy Courses custom themes and show you exactly how to use them. In the next article, we'll detail all of the content available to you, but for now, let's focus on what Handlebars can do. We'll show you all the data you can use with Handlebars very soon.

Handlebars is simple yet very robust. It has a rich community surrounding it that you can learn from. Because of these two facts, we at Zippy Courses chose Handlebars as the tool for empowering designers and developers to quickly create amazing themes in Zippy Courses.

Data in Action

Before we dive into seeing Handlebars at work, it's important to understand how Handlebars works with your site at a basic level.

When someone visits any URL on your site, Zippy Courses determines which template to use based on what they are viewing and then calculates all of the information that the template may need. The template then receives that information and uses it to calculate what the visitor sees.

If you're unclear on what this means in practice, that's okay. Let's see it in action:

Say Hello, Handlebars

Let's look at the most basic example of actually using Handlebars in a template.

Example: <h1>Hello, {{name}}</h1>

You may notice a few things, even in this abbreviated example:

  • We're using HTML to format our content, just like we would on most web pages.
  • There are some curly braces surrounding name. For a moment, let's assume that name is a variable that is equal to Jane.

If we were to load a page using this template, here's what we would see:

<h1>Hello, Jane</h1>

Pretty easy, right? As you may have guessed, the name Handlebars comes from the {{curly braces}} that are used.

So what does {{name}} really mean?

name is a path to a variable, which is a stored piece of information that is shared with your template when someone loads the page.

When a page is rendered on your site, it will load up a template, and when it finds {{name}}, it will know to replace {{name}} with the value it has for the name variable.

Passing information to a template is exciting, but it is the least of what Handlebars is capable of. Let's look at what we can accomplish using conditionals.

Note: Handlebars is smart and will protect you from malicious HTML output. If you ever need to make sure your HTML that has been stored in a path is rendered normally, simply use {{{triple}}} curly braces instead of {{double}}.

Conditionals: If, Else, Unless

When developing a theme, there are times when you'll want to control whether something is visible or not, but you won't know if it should appear until you know who is looking at it. This is called a conditional.

To handle conditionals, you have multiple tools: if, else, and unless. Each of these are a block that has content between an opening and a closing statement.

Let's look at how to use the if block:

{{#if somethingTrue }} <h1>Hello, {{name}}</h1> {{/if}}

So what's happening in the opening statement of our if block? It's asking, "Is the data in the somethingTrue path true?" If it is, it will show everything between the opening {{#if ... }} statement and the closing {{/if}} statement.

Whenever you're working with a block, the opening statement will begin with the normal double curly brace, plus a hashtag (#). The closing statement will have the double braces, plus a forward slash (/).

Note: Handlebars considers multiple types of values as either "true" or "false". Content (words or text that are not empty), any number greater than 0, lists with items in them (such as a course with entries), or the value 'true' are all considered to be true. Empty content ("", with nothing in it), the number 0 or negative numbers, empty lists and the value 'false' will all be considered false in a conditional.

Now what if you want to display something when your variable is not true? In that case, you have two options: else and unless.

Let's look at the else block first, which works with your if statements:

{{#if somethingTrue }} <h1>Hello, {{name}}.</h1> {{else}} <h1>Hello, stranger.</h1> {{/if}}

As long as somethingTrue is true, we will see the value of the name variable. If for some reason, it is false, we will see the results of the else block instead.

Please note that even though we included the {{else}} statement within the if block, the entire block still ends with the closing {{/if}} statement. else works with if, but not independent of it.

Note: The flexibility of if and else can be very powerful, giving you the ability to display content in many different situations.

If you want to do something only when a value is false, you want to use an unless block.

Instead of checking to see if something is true, unless checks to see if something is false.

{{#unless somethingFalse }} <h1>Hello, {{name}}</h1> {{/unless}}

If somethingFalse is false (and we can assure you, it is), then the heading that says Hello will be displayed in the template. Also, notice that the closing tag of the block is {{/unless}} - the opening and closing statements of a block need to match, just like they do with the if blocks.

You can even use unless with else, just like you do with if statements.

{{#unless somethingFalse }} <h1>Hello, {{name}}.</h1> {{else}} <h1>Hello, stranger.</h1> {{/unless}}

Note: Using else statements with unless can get confusing and be a little hard to think about, so we generally recommend using if and else together instead.

Checking for Equality

There are times when working with your template that you will need to know more than whether something is "true" or "false". You'll need to compare one piece of information to another and determine whether or not they are equal.

Let's look at an example that we have in our core themes:

{{#eq settings.payment.type "credit-card" }} {{!-- Part of a credit card form will go here --}} {{/eq}}

On the checkout page, it's necessary to check whether or not the payment settings call for a credit card form or not. We do this by checking the payment type and seeing if it is equal to the value "credit-card". If it is, then we show the form, but if it's not, we skip it.

Loops!

Now that we've gone through how to work with data in our templates, it's time to look at loops. Loops allow you to take a set of data, such as all of the units in a course, and display them one by one, without having to repeat yourself in your template.

After the next example, we hope that you'll appreciate how loops can make your life easier:

    <div class="c-unit">
        <h3 class="c-unit__title">My FIRST unit!</h3>
        <p class="c-unit__description">
            Man, what an awesome unit.
        </p>
    </div>
    <div class="c-unit">
      <h3 class="c-unit__title">My SECOND unit!</h3>
      <p class="c-unit__description">
          This is going to be even better!!!
      </p>
    </div>
    <!-- etc... -->

As each of your courses have a different number of units, but they all use the same template, this can become pretty handy.

You may have noticed that instead of having to use a complicated path to access information about the unit inside of the loop, we were able to simply use {{title}} and {{description}}. This is because Handlebars is super smart and knows when it should change scope. Every unit has a title and description, so it doesn't make you work harder to get to them when you need to.

Just as with our conditional blocks, everything between the opening and closing statements of the loop is displayed. If there were no units for the course, nothing would have been shown.

Including Files

When building your theme, you will come across situations where you want to include other files inside your templates. One of the most common examples is including javascript files and stylesheets. Every page on your site will likely have the same ones, but you don't want to write the same template code over... and over... and over... see our point?

Luckily, Handlebars includes a convenient way for including other files within our templates.

Here's an extremely basic layout template:

<!DOCTYPE html> 
{{> site.head }}
<code><body class="c-page">
    {{!-- Content goes here... --}}
</body>

On Line 3, we are using {{> site.head }} to include the site head component located at components/site/head.tmpl in your theme files.

You include a file by using the following format:

{{> path.to.file }}

We use the opening curly braces followed by a greater than (>), followed by the file path in dot notation, ending with the closing curly braces.

Determining a File Path

Now how do you determine the path to a component using dot notation?

Any time you include a file, you will be referencing a path within the components/ folder of your theme's file structure.

Note: Currently, only components can be included in layouts and other component files. This means you can nest components as deeply or as often as you require, but it is not possible to include one layout inside of another layout.

As you can see in the example above, we are including the site.head file.

In our components directory, this means that we must make sure that the following file has been created:

components/site/head.tmpl

When including files, follow these steps to generate the correct path to the file:

  • Remove components/ from your file's path.
  • Remove the .tmpl file extension
  • Replace all / with .

Once these steps are complete, you can see how components/site/head.tmpl becomes site.head.

Working With Scope

You may remember scope being referenced in the context of loops earlier. As a refresher, let's explore what scope means:

Scope is the specific information available to you and the correct way to access it in a given template file.

Dense, huh? In practice, it's a bit easier. Let's revisit the loops example from earlier but take a step back. Instead of using the loop, let's try it without a loop:

<div class="c-unit">
	<h3 class="c-unit__title">{{course.units.0.title}}</h3>
	<p class="c-unit__description">
		{{course.units.0.description}}
	</p>
</div>
<div class="c-unit">
	<h3 class="c-unit__title">{{course.units.1.title}}</h3>
	<p class="c-unit__description">
		{{course.units.1.description}}
	</p>
</div>
<div class="c-unit">
	<h3 class="c-unit__title">{{course.units.2.title}}</h3>
	<p class="c-unit__description">
		{{course.units.2.description}}
	</p>
</div>
<p>

Pretty repetitive, right? Also, what a pain to remember the fully qualified path to the data for each unit... and because we're not using a loop, we may include too few or too many items.

Here is the same code but in a happier place:

{{#each course.units}}
<div class="c-unit">
    <h3 class="c-unit__title">{{title}}</h3>
    <p class="c-unit__description">{{description}}</p>
</div>
{{/each}}

Instead of having to write the full path of the unit data ( {{course.units.0.title}}), Handlebars changes scope within the block, which allows us to say simply {{title}} instead.

This sounds great, right? You're right, it is... But it's not without one major caveat.

What if you want to access the name of your site from within a loop or conditional block where the scope has changed? In the example above, the unit only knows information about itself, it doesn't know anything about where it lives.

Luckily, there's a way to solve this conundrum:

	{{#each course.units}}
<div class="c-unit">
    <h3 class="c-unit__title">{{title}} for {{@root.site.name}}</h3>
    <p class="c-unit__description">{{description}}</p>
</div>
{{/each}}

By prefacing the variable path with @root, you can go back to the top of your scope and access anything as you normally would.

There are also times when you may want to explicitly define the scope of an included file. Luckily, that's pretty easy to do:

{{> common.blocks blocks=entity.blocks}}

In the example above, you can see that we're including a component file, {{> common.blocks }}. The second half of that statement is where things get fun.

Our entity, in this case probably a page, has some data called entity.blocks, which our common.blocks component expects to receive. The problem is that the file expects it to be called blocks, not entity.blocks.

The solution is to simply tell the included file, "Hey, this is the droid you're looking for," which is what we're doing when we add blocks=entity.blocks to the second half of the statement.

While scope can seem like a scary idea, it will be rare that you need to consider it too hard. When a piece of information isn't appearing like you expect it to, you can look here for advice, but otherwise, it should feel fairly natural to use.

Additional Handlebar Resources

Wow, you read through all that and you still want more? Don't worry, we've got you covered:

  • The Handlebars Homepage: The original resources for Handlebars - this resource will show you everything that Handlebars can do out of the box.
  • The Mustache Homepage: Before there was Handlebars, there was Mustache. Mustache can show you how accessing data in templates works.
  • Advanced - LightnCandy: If you're interested in a deep dive into what Zippy Courses works with for templating, this page shows you the full range. Be warned, it does require an understanding of PHP to fully grasp, but it shows you the gears inside the clock.

What Next?

Now that you know how to use Handlebars, it's time to let you loose with the information to use with it. Zippy Courses does its best to give you all the data you need in your templates to make flexible decisions in your presentation.

Let's take a look at Data in Zippy Courses Templates so you can see what's at the buffet.