What do you want to learn?
Skip to main content
by Ryan Lewis
Start CourseBookmarkAdd to Channel
Table of contents
Handlebars: An Introduction
Why Are Templates Needed?
If you're watching this course you've likely constructed a website at one time or another or at least understand what goes into building a website. Websites are designed to communicate information visually to human beings staring at some type of screen. The mechanism of getting a computer to display the information the developer wants in a structured way is a programming technology called Hypertext Markup Language, or HTML. This language has been used on the web and interpreted by applications called web browsers since the early 1990s. Even in the early days of HTML, developers realized that in the act of creating the code to display their desired information there was a lot of redundancy in the markup. Oftentimes, they would use the same markup to display different information, causing them to type or copy and paste the same code over and over again to create a multi-page website. If a change was needed to the look of a website, code would have to be changed in multiple places, making the probability of errors quite high. At some point, developers realized that if they used a template pattern, writing their HTML markup once, and then populating it with different data, the stability of their websites could be greatly increased and redundancy decreased. Using templates made it easier to build websites quickly and make changes. Rendering HTML with templates quickly became an industry standard. And despite template-rendering technologies coming and going, the practice of using templated HTML has only grown.
The Client-side vs. Server-side Debate
One of the great developer flame wars has been around client-side versus server-side rendering of HTML templates. Both sides agree that templating is the correct approach, but where to do it is what's in contention. In the early days of HTML templating, server-side rendering was the only option. But now that browsers are becoming more and more powerful, client-side rendering has quickly become an attractive option. First, just a quick recap on each. Server-side rendering means that the markup that the user sees is dynamically created on the web server and delivered to the browser ready to go. Client-side rendering means that a template and data is delivered to the browser and then rendered and displayed for the user. Here's what server-side rendering proponents say about client-side rendering. Requiring your presentation to be rendered using inconsistent browser specs puts an unneeded strain on low-powered clients, especially mobile devices. Making the user wait while the templates render at the beginning of the app is unacceptable. And there are still some issues with getting client-side rendered apps to be optimized for search engines. The client-side rendering supporters have a similar list of arguments. Rendering on the server side means sending redundant markup to the client, costing bandwidth and increasing page rendering time. The responsiveness and user experience of a client-side rendered application surpasses the slow, wait-for-it-to-load nature of a server-side rendered app. Both sides have completely valid points, and I believe a complete solution uses both. Search engine optimization is a real consideration that rendering completely on the client makes extremely difficult. In addition, an instantly rendered initial page load is a better first impression than a blank page. So the ability to render on the server for those scenarios is a huge boon. On the other hand, rendering on the client is basically a standard for using any client-side framework, and the speediness of click and it's there is practically expected by users today. Client-side rendered applications from large companies such as Google only embed the UX expectations further into users' minds. I recommend an approach that utilizes rendering on both sides, delivering an initial page that is server rendered and any subsequent pages be rendered on the client. Handlebars, the templating library we'll learn today, is fully functional on both sides of the rendering divide, allowing you to decide what's best for your application.
The Mustache Philosophy
The Mustache templating specification was first released around 2009 and was first implemented in Ruby. Over the years, it has been implemented in nearly every popular programming language. The specification itself is only around 1000 words, and that minimalism is part of its success. Handlebars is an implementation of the Mustache spec and carries on much of the philosophy and syntax of vanilla Mustache. Mustache proposes to keep your logic out of your templates. There are no logic sections such as if and else or for loops, only Mustache tags that are replaced by values. This makes Mustache dead simple to understand and usable in an endless variety of situations. Why have logic-less templates, you may ask? It's all about separation of concerns. Mustache and templates in general are used to create the presentation layer of an application. The main concern of templates is how something looks, not processing and branching to choose what to display. Keeping application logic separate from presentation markup is a best practice. Separation of concerns is primarily motivated by the simplification of application development. Having a single place to change your interface makes it much easier to maintain and reuse for other parts of your application, as well as testing.
Birth of Handlebars
What It's All About
That's it for module 1. We started the module discussing the problems that templating solves, improving modularity and reducing redundancy. Then I discussed some pros and cons of client-side and server-side templating. In many cases, using both is actually the best option. Then I revealed the Mustache philosophy and described the shortcomings, which Handlebars overcomes. I gave you the low-down on Handlebars, setting you up to be successful once we start learning the code. We installed Bower, our client-side package manager. And finally, we pulled down the Handlebars dependency and wrote a little Hello world application with templates. In the next module, we're going to start learning Handlebars syntax. Did you think it was going to be as simple as tags? Oh no. We'll learn about paths, blocks, lists, partials, and oh so much more. We'll also start working on our demo application, which is going to have super cute dogs because I like them. And I wanted to say dog about 100 times in the next hour and a half. So, see you in the next module.
Building Blocks of Handlebars
Demo Application Walkthrough
Handlebars, jQuery, MDL
Besides Handlebars, we'll be using a few other dependencies to make our apps sing. We'll use Bower to pull these down just like we did with Handlebars. Now, you may have started a new folder with the starter code so we'll want to download Handlebars again as well. And since we're going all in with this app and trying to use best practices, let's use Bower the best way. In your command line, navigate to your app directory. What we want to do is use a bower.json file to help manage our dependencies. What this file does is keep some metadata about your app, as well as the dependencies that it uses and the versions. This makes it easy to keep dependencies aligned between team members by sharing this file. If you're familiar with npm, this is basically the Bower equivalent of a package.json file. Execute the command bower init to have Bower generate the bower.json file for you. It will run you through some questions about your app. Just answer whatever you want here. We won't actually be using this data anywhere. Once it's complete, your bower.json file has been created and you're ready to start installing dependencies. There are three dependencies we need, Handlebars for our templating, jQuery for DOM manipulation and utility methods, and Material Design Lite, a CSS framework from Google that will provide a lot of the styles for our site. With Bower, you can chain together installations so let's do that. And one more thing, we will be using Handlebars version 3.0.3 in this course. So, we'll specify the Handlebars version when installing. In your command line, type bower install --save jquery handlebars#v3.0.3 material-design-lite. That's l-i-t-e. This will install all three of our dependencies in one go and save them all to our bower.json file. One key thing to know about Bower, it uses the Git protocol to access repositories on GitHub. If you're at work or behind a firewall, there is a possibility that the Git protocol may be blocked. If you encounter any issues when trying to install something from Bower this may be the cause. To resolve this, configure your Git installation to use the HTTPS protocol instead of Git. Execute this command into your command line to change this and then retry the bower install command. We've got all three of our external dependencies lying in wait in our bower_components folder. In the next clip, we'll start hacking on our demo application.
Localization with Handlebars
Iterating Through Arrays
I've talked up helpers quite a bit so far, but we'll wait until next module to get deep into them. Handlebars does ship with some built-in helpers however, and we will cover those in this module. One of the most used helpers is each. The each helper is used to iterate through arrays or objects. The each helper takes a single parameter, which is the object or array to iterate over. Inside the each block, the context becomes the current element that is being iterated over. Here's an example of using each with an array. Assume that you passed in a property into your template called dogs. Dogs is an array with a few names of dogs as strings. To iterate through these, you would call the each helper and pass the dogs property as the first argument. Close the each block with a forward slash. Inside the each block, you would reference the current element with the this keyword. In the case of an array containing strings, this would output the contents of the current string. And your output would look just like so. But wait, there's some weird stuff here. What's that hash sign? What is this argument doing in a Handlebars expression? Let's cover a few points before going any further. Beginning a Handlebars expression with the hash symbol indicates that you'll be creating a block. Handlebars blocks begin with an expression with the hash symbol and end with the same expression except starting with the forward slash, similar to HTML. The first property after the hash and forward slash must be the same. This is how Handlebars matches up the opening and closing elements. You can make a block with a single property like an object, and the context inside that block will be the object itself. With the built-in each helper, the hash is used to create a block that will loop for each element in the passed array or object. I also mentioned arguments. Handlebars helpers are able to have arguments passed into them. The first argument of a Handlebars expression is the property or helper name. So, a Handlebars expression to render a single property like our site title would have only a single argument, which is siteTitle. Handlebars would find the data for this property and render it. If the first argument is a helper instead then it will take other arguments in the expression, each separated by a space. We'll do some work with custom helpers and arguments in the next module. And for this module, when I say argument in the context of a Handlebars expression that's what I'm talking about. Alright, back to it. What if we're iterating through an array of objects. This is just as simple as the last array example. On each iteration the context inside the each block is the current element. You can reference properties on the object simply by using the property key in an expression. With the following data example, we could output the name by just using the name property. The image property could be input inside this image tag just like so. You can explicitly use the this keyword to validate these properties, but it's not required. Now that we know how to iterate through an array, what could we do with this knowledge? Well, we've got a total of around 20 dogs that we want to show in our site. Each one has the exact same markup except for a few data points. Looks like a perfect candidate for an each block. So let's go into index.html, and we're going to make a script block to store our dogs template. So, insert the script tags at line 149. We'll give this the same type as above, text/x-handlebars-template, and an ID of dogs-template. Inside this expression, we're going to use an each block. So, create that. The argument for the each expression will be dogs, an object we aren't passing into our template just yet. Now we need to get the markup. In the index template at line 54, we see a div with ID of theDogs. This is our parent div for all the dog elements. We will eventually want to render into this div. For now, look at the first child element below it. On line 55, there is the start of a div block with the class dog-card. This is the HTML to represent one of our dogs on the screen. If you scroll a bit down, you'll see that this markup is replicated multiple times with different data. We will extract the HTML for a single dog and use that in our template to render all the dogs dynamically. Copy lines 55-67 and paste the template inside the each block of our dogs template. When dealing with Handlebars tags in HTML, I typically indent as if the Handlebars segment was an HTML segment. This can sometimes get a little weird with ifs and elses, but generally works out fairly well. With the markup for our dog-card entered in the template, we can now swap out the data points for Handlebars expressions. On the right, here is the data structure for each dog. This is defined and set up in the dogPack.js file. First, let's take the ID value on line 151 and replace it with a Handlebars expression in the ID property. In the style attribute on line 152, you'll see that the background URL is set to an image. These are all in the images folder of the starter code, and each dog object has an image property for the JPEG that corresponds with that dog. Replace goofy_dog.jpg with image and a Handlebars expression. And in the next line, swap out Goofy for a Handlebars expression with the name property. The next two Handlebars expressions will be in reference to our localization and won't actually come from our dog object. We will be passing in the global language object when we render the dogs template, but how do we reference an object at the root context inside an iteration of an each block? Well let me introduce you to the first @data variable that we'll use for Handlebars. @data variables are used by Handlebars to signify special properties to be used in built-in Handlebars helpers. To get the root context of an each helper while inside an iteration, use the @root variable. This will allow you to access the initial context that the template was rendered in. This can be especially useful when we want to access the language property. On line 157, replace dog with a Handlebars expression. Inside this, we'll insert @root.language.yep. In this way, we access the language property that was part of the initial calling context. Replace line 160 with the nope property on the @root.language object. Our template is complete. Great job. Let's get rid of all the dog markup inside our root dogs element. Delete the HTML from line 55 to line 132. You should be left with a single div with the ID of theDogs. This is where we will render our dynamic dogs. Let's go ahead and write the code to render the template. In app.js, we'll first call renderDogs, which is a function we'll build below. On line 4, call renderDogs below renderPage. On line 17, declare the function renderDogs. This is where we'll put all our logic for rendering our dogs template. We'll do our Handlebars dance like we did in renderPage by first getting the template. Declare the template variable and use jQuery to get the HTML from the element with ID of dogs-template. Next, we'll create our compiled variable and call Handlebars.compile with our template. The last piece will be our rendered variable. Call the compiled function, and into this we need to pass two separate variables to be rendered, dogs and language. To do this, create an object literal and give it two properties. The first is dogs, to which we'll give the value of DogPack.dogs, the dogs that I've initialized for you already. The second property is language to which we'll set window.language. This will render our template with the correct data. Our last step is to insert the rendered HTML into our page. Use jQuery to select on the element with ID theDogs. Then, set its HTML to a rendered variable. Now, let's check out our work in our browser again. If you reload the page, all of our changes should be run and our page should work. It looks good to me. If I scroll down, I see a whole lot of dogs just as I had hoped. This means we're rendering all the dogs that are in our dogs object, which is good for now.
Logic for your Logic-less Templates
One of the key tenants of the Mustache spec, its tagline if you will, is logic-less templates. Handlebars ships with the if/else helper functions, which gives your templates just a smidge of logic. Wait, you may say, doesn't this totally break Mustache? Isn't this heresy by Handlebars? Calm down. Let me explain. Mustache actually does define all the mechanisms to make something like if/else work. By this I mean Mustache also has just a smidge of logic, a Mustache block section where the property looks like this. The templated expression inside will only execute if that property exists. So, if it exists then the template inside will execute. Next, a Mustache specification also defines an inverted section, which is basically the opposite of the if block. If the property doesn't exist then execute the code in the block. These two structures are basically an if/else structure. It is limited to the existence of properties or Boolean properties, but it is still fundamentally a piece of logic. Handlebars doesn't beat around the bush, and instead it just gives you what you want with an if/else helper. I believe this is an acceptable amount of logic in your templates and when used correctly will only help to separate your application logic from your template. Let's look at the syntax of an if/else block. The Handlebars expression begins with a hash sign. Then, the first argument is the if helper. The next argument is the property or Boolean to check for truthiness. The following templated code will render if the argument is truthy until a closing if expression or else expression is found. This is what a basic if block looks like. The if helper is limited in what you can check for truthiness. It only accepts one argument so you're not able to do any comparisons or operations. You can only check one property and the check is its truthiness. In the past, I've implemented more complicated if statements, but I've always had to use helpers to accomplish it. I also ended up questioning why I needed such complicated logic and ended up refactoring it out later back into the application code. Sometimes this limitation to what can be compared will actually help guide your software design decisions. Else adds another dimension to your if blocks. When entering the expression inside an if block, it will render the template after an else expression if the argument evaluates to falsy. Here's the previous example with an else expression giving us an alternate section to render based on the state of the object. Else can also be used with the each helper. If the argument to an each helper does not exist, the template markup after the else expression will be rendered instead of what is above it. Here's an example of an each block for dogs and the message that the else block would show if dogs evaluates to falsy by either being undefined or if it's an empty array. Let's take the if/else helper for a spin with our application. Switch over to your text editor and open up the index.html file. In our dogs template, we are iterating over a dogs property, but what if there are no dogs? Well we shouldn't have that situation yet, but once we add filters it will become a possibility. So let's add an else expression to our each and print out a message if there are no dogs. After line 84, make a new line and add an else Handlebars expression. After this else expression we'll output the language.noDogsMessage property, which is a localized message. We don't have a way to test this out just yet, but we will later when we add filters. Now let's add a full if/else to our project. We'll hook up our DOG and NOT buttons to actually work with our application and use an if/else helper to give the user some feedback on their choice. First, we'll hook up the buttons to modify our dog objects. Open up app.js. One important thing to remember about rendering templates with Handlebars, if you want to have click handlers or interactions attached to DOM elements these handlers need to be set each time you render, not just at the beginning of the page load. Every time you replace the DOM with the newly rendered HTML the old click handlers fall off. So let's make a function that will set all that up for us. Call it attachDogButtons and define it after renderDogs. First, let's grab all of the dog buttons with jQuery and add a click handler. Inside the click handler, create an anonymous function. The first thing we'll do is to find out which dog was actually clicked. Declare an ID variable to store the dog ID. Then we'll use jQuery to select the element that was clicked and then move up the chain to find the dog-card element that it is a part of. This element has a data field on it that was populated by our template. Use the data function with the string dog-id to get this ID field. Now that we have the ID, pass it into the DogPack.chooseDog utility function. This will set the chosen property on the dog to dog. The last thing we'll do in this handler is to re-render our dogs. Call renderDogs here. Now we'll add our not dog click handler, which will look suspiciously close to the dog click handler that we just implemented. For this reason, copy the dog-button click handler and paste it below. Change the selecting class to not-dog-button. And instead of calling DogPack.chooseDog, change it to DogPack.chooseNotDog. I really hope you're impressed with my function naming skills. I do tend to go a little overboard. We're still missing something if you remember what I mentioned about attaching button handlers. If we just call renderDogs it will refresh our dog code, but the click handlers will be gone. So, add an attachDogButtons function call to the end of renderDogs so this action will happen every time we render our dogs. Alright, so we've got click handlers modifying our underlying data, our render functions are set up to attach all the click handlers so any time we see some dogs we will be able to evaluate their dogliness or lack thereof. Now let's use our if/else handler to display feedback to the user. Back in index.html, find our dogs template and make a new line at line 83. Here we will insert the user feedback on whether their selection was correct or not. Our first if block will determine whether a choice has been made or not for that dog. The chosen property will not exist if a choice has not been made. Create an if helper block and use chosen for the first argument. Anything inside this if block will only execute if chosen evaluates to truthy. Now, we will make our binary choice to determine if the user selected the correct option. All the logic to determine this is in our dog object so we don't need to know what's going on in our template, which is exactly how it should be. Create another if block and pass the isCorrect property as the argument. Go ahead and add an else expression as well to complete our helper structure. If isCorrect is true, we'll utilize a localized message by rendering an expression with the @root.language.correctInd property. If isCorrect is not true, anything after the else will execute. And here we'll output the @root.language.incorrectInd property. To add a little styling for each of these I've already created some classes we can apply. Around each of the tags we just added wrap them in a span block. And for the correct message use the result-good class. For the incorrect message, use the result-bad class. Now we're at a place where we can go check out our progress. Move over to your browser and refresh that puppy. Again, you should see no changes, which is good. We're changing so much structurally with the page we don't actually want the visuals to change. Now click on one of the DOG or NOT buttons, and based on your choice you should see a feedback message telling you if you're right or not. Go ahead and try out both right and wrong choices. I promise no one will judge or think you're stupid for saying a bunny is a dog. If everything is looking good go ahead and continue on to the next clip.
Modularization with Partials
Escaping HTML and Removing Whitespace
Handlebars, by default, escapes HTML text when rendering it through expressions. This means that if you enter any special characters in a property Handlebars will convert it so special characters will be displayed as text instead of interpreted as HTML. This works fine for most scenarios like when you want an ampersand to look like an ampersand or want to output some HTML example for a Pluralsight class on Handlebars. But what if you want to output actual HTML? The default way of using Handlebars expressions won't work. Instead of a normal expression, you can add an additional set of curly braces around an expression and it will let raw HTML pass through. So if I have a property with this data that has HTML in it and I render it with a normal expression it will output like this with the HTML characters escaped. But if I use the triple curly brace expression then the raw HTML will output like so, which is exactly what I want. White space is another consideration when rendering Handlebars templates. With all the if/else blocks, iterators, and property rendering sometimes your rendered HTML can turn out a little, well, Frankenstein-ish. Luckily, Handlebars provides a very simple remedy for this. You can begin or end any Handlebars expression with the tilde character, and this will remove any whitespace or new line characters between the expression and the next non-whitespace character. You can liter these throughout your templates as needed or just go wild and put them everywhere. There is just one consideration with this, it only affects the rendered look of the HTML code, not how your site actually looks in a browser. You will likely be the only person looking at your HTML code so it's really only for your benefit. Still, having control over these types of concerns is a very nice thing to have. I don't have any exercises for these features so just keep them in mind when you're working on your own project as they might come in handy.
Great job making it through this module. I know syntax overviews can be a little dry, but honestly, we've covered most of the Handlebars syntax already. Let's review what we covered in this module. First, I introduced you to the demo application we were going to convert, and we installed our application dependencies with Bower. Then we went over basic Handlebars expressions and started the first conversion of our application by localizing much of the text on the site. We looked at the each built-in helper and started iterating through some dogs. You know, I just realized that I've said dog almost 150 times in this module alone. That's pretty cool. It's like some sort of Pluralsight record or something. Anyway, we learned that Handlebars stretches the logic-less rules a bit with the inclusion of if/else helpers. Then we implemented a partial and learned a few of its tricks. Finally, we looked at how to output raw HTML and handle whitespace in Handlebars, two very useful bits of knowledge. In the next module, we're going to go deep into helpers. They'll really bring our application to life and rival a lot of the functionality in most modern-day web frameworks. Helpers are pretty fun to work with and finding all the places you can use them can sometimes feel like an Easter-egg hunt. There are so many uses. We'll create several so you can feel comfortable using them in your own site. See you in the next module.
Harnessing the Power of Helpers
Function Properties in Handlebars
The Case for Helpers
Dual Citizenship Helpers
Helpers Inside Helpers
New Helpers on the Block
Beyond the Basics
Modular Template Patterns
Template Precompilation with Gulp
Handlebars + node
Handlebars in the Wild
Released30 Sep 2015