2016-08-07

Precompiled Handlebars - A Ridiculously Clear Tutorial

This is a step-by-step tutorial on getting introductory, hello-world level, precompiled Handlebars up and running.

Just so you know, if you're already fluent in NPM or otherwise want to skip ahead, you can jump to the good stuff

It's my first time writing a tutorial like this. I plan to take you through it very slowly, but very clearly, so that even beginners can understand if they have some programming experience, but little to no web or templating experience. If this style of tutorial proves helpful to people, I'll follow up with a second tutorial on more in-depth Handlebars features. I encourage you to send any feedback that will improve this tutorial. Now let's dive in!

For those who read my recent post about a "real" side project, I know I promised a more detailed explanation so I'll tack it on at the end here. ;)

First, some context. What you're reading was born partly of frustration. I had a needlessly difficult time figuring this out for myself, and I want it to be easier for others in the future. Therefore, today's post has become three things: a precompiled Handlebars primer, a learning experience post-mortem, and an experiment in writing effective tutorials for beginners, by recent beginners. Handlebars isn't hard to learn, the concepts are approachable; but existing tutorials, StackOverflow Q&As, even the Handlebars team's own documentation assume you already know web dev and templating, so their tutorials give condensed steps for experts to hit the ground running.

Not much help for a beginner like myself. Therefore...

Handlebars for Beginners, by a Beginner

I'm going to take you through the steps I had to take to get this project moving, from the perspective of someone with beginner-level knowledge of web development, and zero knowledge of templating. I won't leave any of the steps out or assume you know any of them, and I'll try to cover the minutiae and details that many tutorials gloss over, either because they figure you already know them, or because it doesn't even occur to them that you might not know them. I also don't like tutorials that speak only to Windows users when I'm stuck on Unix for work, or vice versa, so I'll try to cover these cross-pollinations as well.

The only thing I won't explain in detail is installing NPM, the Node Package Manager. You'll need to have that running in order to precompile. It is relatively easy to get NPM going, here are installers/binaries for Windows, Mac, and even Linux.

Just so you know, the installer is intended to automatically add NPM to your PATH variable, but in some cases an error may prevent that. I'm tempted to describe the fix for this, but due to the number of operating systems out there, it's beyond the scope of this tutorial. Just be aware of this if you install NPM and it doesn't work, and it's not immediately obvious why.

Brass Tacks

First, you must install Handlebars for Node.js via NPM. From the command line (any directory is fine), issue the following command. Note for Windows users: think of the '$' symbol as your 'C:\>' or other command line prompt.

$ npm install -g handlebars

Now you should be able to run Handlebars on the command line from any directory. To test, try typing the following, and you should be rewarded with your installed Handlebars version, in my case, 4.0.5:

$ handlebars -v

4.0.5

That is all the infrastructure you need. Now I'll take you through the process of building a few files that you'll need. This is one place in particular I find that other tutorials fail to be clear, so I will do my best. First of all, I'll assume for clarity everything is taking place in the same directory, so make yourself a directory and keep all files there. I encourage you to improve your infrastructure and employ best practices, but later. First let's make sure we understand this technology.

We will end up with a total of 4 files:

  1. We will make a Handlebars temmplate file, with the .handlebars file extension (not strictly required, but consider it so for the purposes of this tutorial)
  2. We will make an HTML file to inject our template into
  3. We will download or link to the Handlebars library provided by the Handlebars team
  4. The Handlebars precompiler will output a JavaScript file which performs our template injection (details to follow, don't get intimidated yet if this sounds foreign or complex)

Some folks will likely argue the best practice is to link in your code to an outside source for your Handlebars library (file 3). That may be so, but to make your life easier, I recommend downloading it and keeping a copy locally for the purposes of this tutorial. You can get it here. The version I got is called handlebars-v4.0.5.js.

File 4 is output by the precompiler, so we won't worry about that yet. Let's start by making a very, very simple template (file 1), to inject into our HTML file (file 2). What you need to know for now, is that a Handlebars template is basically made up of a mixture of raw HTML, and special Handlebars tags (discussed later). What that means, is that we can get started with an extremely simple example template containing just HTML, to make sure we have a grasp on the concepts, and that we're doing it right. So here's file 1, in all its young glory:

hello.handlebars File 1
  1. <p>Hello, world!<p>

Yup, that's it for file 1. Precompiling is very simple and straightforward, assuming Handlebars is installed properly. What you need to know for now, is that Handlebars takes .handlebars files and turns them into .js files. So let's use Handlebars to turn hello.handlebars (file 1) into hello.js (file 4):

$ handlebars hello.handlebars -f hello.js

... or more generically ...

$ handlebars <input-file>.handlebars -f <output-file>.js

Note that the Handlebars precompiler doesn't give you any real feedback on the command line. Just check that you got a .js file out of the deal, and that it contains some JavaScript in there, including your HTML. At this point, if all has gone well, we should have the following files, all in the same directory:

  • File 1: hello.handlebars Handlebars template file
  • File 2: not done yet
  • File 3: handlebars-v4.0.5.js or version of your choice
  • File 4: hello.js output from file 1

So all we need to do now is put together a very simple HTML file into which our template will be injected. Note that in this case, I'll be including some JavaScript in my HTML file. Normally, it may be wise to separate your JS from your HTML files, but for simplicity I'll keep it all together for this tutorial. Let's start with a very basic HTML file and build our way up from there. I'll highlight changes at each following step in green, and describe the changes.

hello.html File 2
  1. <html>
  2.   <head>
  3.     <meta charset='UTF-8'>
  4.     <title>Handlebars Tutorial</title>
  5.   </head>
  6.   <body>
  7.     <div id='content'></div>
  8.   </body>
  9. </html>

If you're new to HTML, most of this should still be approachable. Just note the <meta> tag, which tells the browser what character encoding the HTML file uses; and the initially empty <div> tag, which warrants more explanation (but for now, don't overthink it; it's a generic container for part of a page's content).

Nothing really special going on here, if you load this in a browser now, it'll just be a blank page. Just take note that <div id='content'><> is the place in this HTML that I have chosen to inject my Handlebars template, which is why I gave it an ID.

Now I'm going to add in some vanilla JavaScript, whose job is singular: tell the browser not to try doing anything with any scripts, until all of them are loaded. (If you want to do this using jQuery or other more advanced methods, I encourage it, but the point of this exercise is to be simple and to-the-point, so for now, we'll stay vanilla.)

hello.html File 2
  1. <html>
  2.   <head>
  3.     <meta charset='UTF-8'>
  4.     <title>Handlebars Tutorial</title>
  5.   </head>
  6.   <body>
  7.     <div id='content'></div>
  8.     <script type='text/javascript'>
  9.       function init() {
  10.         // This only runs after the whole page is loaded
  11.       }
  12.       window.addEventListener('load', init, false);
  13.     </script>
  14.   </body>
  15. </html>

Simple enough to follow, I think. Now, let's add in the scripts themselves, that the browser will load before running our init() function.

hello.html File 2
  1. <html>
  2.   <head>
  3.     <meta charset='UTF-8'>
  4.     <title>Handlebars Tutorial</title>
  5.   </head>
  6.   <body>
  7.     <div id='content'></div>
  8.     <script type='text/javascript' src='handlebars-v4.0.5.js'>
  9.     </script>
  10.     <script type='text/javascript' src='hello.js'>
  11.     </script>
  12.     <script type='text/javascript'>
  13.       function init() {
  14.       }
  15.       window.addEventListener('load', init, false);
  16.     </script>
  17.   </body>
  18. </html>

Magic Happens

We're going to inject our template with just 3 lines of code. You can do it in fewer, but for clarity I'm spreading this over more lines. I'll explain this process line by line following the new code below:

hello.html File 2
  1. <html>
  2.   <head>
  3.     <meta charset='UTF-8'>
  4.     <title>Handlebars Tutorial</title>
  5.   </head>
  6.   <body>
  7.     <div id='content'></div>
  8.     <script type='text/javascript' src='handlebars-v4.0.5.js'>
  9.     </script>
  10.     <script type='text/javascript' src='hello.js'>
  11.     </script>
  12.     <script type='text/javascript'>
  13.       function init() {
  14.         var target = document.getElementById('content');
  15.         var inject = Handlebars.templates['hello'];
  16.         target.innerHTML = inject();
  17.       }
  18.       window.addEventListener('load', init, false);
  19.     </script>
  20.   </body>
  21. </html>

Line 15: This is the easy part. We are just creating a JS variable to represent the $lt;div> we're going to inject our template into.

Line 16 Here we are grabbing our template, and putting it in a variable called inject. There are a few things here worth knowing. First of all, the reason we have access to the template at all, is because we included the hello.js file we precompiled with Handlebars on the command line (file 4). We have access to the Handlebars variable because it is provided by handlebars-v4.0.5.js, file 3, which we also included. As a convenience, Handlebars allows us to refer to our precompiled script by file name, without needing the extension (e.g. 'hello' instead of 'hello.js'). The last thing that is critical to understand, is that inject is now a function, not a string or other variable type. You must therefore use it as a function, and that function returns the precompiled template as a string.

Line 17: Now that we have our target, and our injection function, we can inject our template into the target. We call our template function, and assign it to the innerHTML property of the target. Done! If all has gone well, you should be able to load your HTML and see your simple HTML template loaded into an otherwise blank page: "Hello, world!"

Celebrate

This write-up has given me a great opportunity to review what I learned in teaching myself Handlebars, and I hope this tutorial, written from the perspective of a web templating beginner, for other beginners, was helpful.

Thanks for reading!
- Steven Kitzes


CYOAG

I promised to describe my side project, so I'll take just a moment to do that. (It'll be a quick moment, because that's all I have to spare at this particular moment, but I want to fulfill my promise of sharing my idea.)

In short, it's a user-generated Choose Your Own Adventure game. Many of us enjoyed these books as kids. The basic idea for those of you who are unfamiliar, is that you read a chapter, the book lets you make a decision for what the characters in the story should do, and you flip to different pages based on your choice and get to see different outcomes. I basically want to create a web-based version of this, where users not only get to see different outcomes based on their choices, but also get to contribute their own branches and paths to the story.

Of course, with users able to generate content, you could potentially have dozens of possible paths out from any given chapter (or 'node' or 'snippet', as I call them). In order to combat the potentially overwhelming number of options available, I plan to implement a voting system, similar to the ones used by Reddit or StackOverflow (but without the complexity of decay, though I may add that in later if needed... not likely). Basically, all paths will be available to any user by request, if they want to read and vote on them; but by default, the user will only be shown the top four most popular paths.

That's all I can give you for now, not because I don't want to share more, but due to time restraints... gotta jet! I've cobbled together a first-pass functional requirements doc for this project, so if you want to learn more, it's public on my project's GitHub page. You can check out all the gory details here!

No comments: