Using Inline Partials and Decorators with Handlebars 4.0

Ryan Lewis
Share

Handlebars is one of the most widely used JavaScript templating libraries for both client-side and server-side rendering. It implements the mustache specification but adds some extra sauce to make working with templates easier. If you’re new to Handlebars and want to know more, I suggest you to check out my Pluralsight course on JavaScript Templating with Handlebars to learn how to get started.

Handlebars version 4.0 landed in September 2015 and brought two major new features with it: Inline Partials and Decorators. In this article, we’re going to take a look at both features, explain their syntax and when you should use them. By its end, you should feel comfortable using both features to take your templating game to the next level!

Inline Partials

Partials are a common templating concept not unique to Handlebars. The idea behind it is to create templates that are likely to be re-used, separate them into their own file (a Partial), and then use them in different templates. You may think at Partials as a simple a tool to modularize your templates.

In Handlebars, Partials might not be the most convenient structure to work with. First of all, all partials are global. That means this tool may work for your application, but having little control over it could become a problem in large applications. Secondly, partials are required to be registered using JavaScript. Many template pre-compilers or loaders will handle this for you, calling Handlebars.registerPartial(). Finally, partials have to be separated from the templates where they’re being used. This can be a boon if your templates are large, but can also make it difficult for developers to fully understand the output of a template. They’ll need to switch between many different files before understanding the full output.

All these issues shape the way developers use partials. They end up being reserved just for the largest chunks of reusable code.

With Inline Partials, Handlebars finally releases the true potential of Partials, enabling you to drop JavaScript and the necessity to split partials into separate files. Inline Partials are defined inside your templates, in Handlebars syntax. There is no JavaScript required to register them. You just declare a partial and use it. In addition, they aren’t global but block-scoped. This means that once you’ve declared an Inline Partial in your template, it can only be used in the current scope and any below it.

When deciding to use an Inline Partial or a normal Partial, look for small, re-usable chunks of HTML code that have either of these properties:

  • They’re too small to deserve to be in their own partial file.
  • They are (or can be) used just in the context of a single template.

Using Inline Partials

Now let’s take a look at Inline Partial syntax and usage.

Here’s how you declare an Inline Partial. First, take the code you want to be your partial.

<li>I'm iteration #{{number}}</li>

Then wrap it with the new inline syntax, passing one argument which is the name of the partial.

{{#* inline "iterationCount"}}
    <li>I'm iteration #{{number}}</li>
{{/inline}}

You can now use this partial in the Handlebars template where it was declared. Here’s a complete example.

{{#* inline "iterationCount"}}
    <li>I'm iteration #{{number}}</li>
{{/inline}}

{{#each someArray}}
    {{> iterationCount}}
{{/each}}

Simple Partials Example

With the previous explanation in mind, the next step is to understand how we would use Partials before we had Inline Partials. Let’s assume we start with this template:

// template.hbs
<h1>Hello {{firstName}} {{lastName}}</h1>
<ul>
    {{#each clients}}
        <li>{{firstName}} {{lastName}}</li>
    {{/each}}
</ul>

The repetition of {{firstName}} {{lastName}} opens up the opportunity for typos and errors. The task to accomplish is to extract that pattern into a partial, so let’s see what we have to do in order to achieve it.

First of all, you create a JavaScript file, for example someFile.js, with the following code:

Handlebars.registerPartial('fullName', '{{firstName}} {{lastName}}');

Then, in you Handlebars template you can have:

<h1>Hello {{> fullName}}</h1>
<ul>
    {{#each clients}}
        <li>{{> fullName}}</li>
    {{/each}}
</ul>

While this does clean up our template and make it more idiomatic, it obfuscates the implementation of the fullName partial into a separate file (using a different language and syntax). A developer coming to this code for the first time might face some troubles trying to understand an entire template if many of these small chunks of templates were refactored into partials.

Inline Partials Example

Now let’s take the previous example and solve it with Inline Partials. You’ll notice a few things in the next example:

  • Everything is in the same file and same language.
  • Template scoped partial means you can have a different “full name” format in another file.
  • Keep the same benefits of normal Partials, such as removing redundancies.

The following is the full name solution using Inline Partials:

// template.hbs
{{#* inline "fullName"}}{{firstName}} {{lastName}}{{/inline}}
<h1>Hello {{> fullName}}</h1>
<ul>
    {{#each clients}}
        <li>{{> fullName}}</li>
    {{/each}}
</ul>

Decorators

In the introduction of this article I’ve mentioned another big feature in Handlebars version 4.0, Decorators.

Decorators let you “decorate” the Handlebars program function and modify states before rendering a template. The primary goal is to allow you to use non-output “metadata” to add functionality to your templates. The implementation is based on Yehuda Katz’s JavaScript Decorator proposal for ES6. In many ways, Decorators in Handlebars provide you with a companion to helper functions at a more fundamental level. In fact, before their introduction, you may have been using helpers to achieve what now is elegantly done by Decorators.

To understand where Decorators fit in Handlebars template rendering, let’s have a look at how Handlebars compiles templates. The “Handlebars Dance”, as I like calling it, does something like:

  1. Getting the template
  2. Compiling the template
  3. Rendering an output

In these three steps, the second one is executed by calling the Handlebars.compile function. It takes a template as a string and compiles it, returning a function that you can then call with some context data (the third step from above). Each block in your Handlebars template creates one of these compiled functions, and the main one that is returned calls them as needed to render your output.

Decorators interject themselves into these block-scoped compiled functions, giving you control to execute some functionality before the block is rendered. What you do with it is up to you, but the return value that a Decorator expects is a function that would render a template output.

Before looking at the Decorator function arguments, let’s examine a simple instance.

Using Decorators

Decorators are registered in JavaScript, like helpers and partials (not Inline ones, though!). Here’s an example of that:

Handlebars.registerDecorator('shhh', function(program, props, container, context) {
    var isLoud = program().trim() === 'loud';
    if (isLoud) {
        return function() { return ''; };
    } else {
        return program;
    }
});

In the above example, we look at the Handlebars program function (I usually call this “the compiled function”). If the program returns “loud”, then we will overwrite it with a function that returns an empty string. Otherwise, we will return the normal program function.

Let’s see how this Decorator will be used:

loud
{{*shhh}}

With this template example, the original program function will return “loud” (Decorators have no output). And the output of this template when rendered will be:

That’s right, just an empty string.

The function whose scope is to render the template that has been “decorated”, which is returned from the “shhh” Decorator, returns an empty string. That function is returned based on the truthiness of “loud”.

Let’s now look at a different template:

quiet
{{*shhh}}

The output when rendering this template would be:

quiet

Since the program did not match “loud”, it was passed through instead of being overwritten.

This is an extremely arbitrary example, but hopefully you can see how Decorators affect the program function and how powerful is having control over it. It’s now time to see the Decorator function arguments.

Decorator Function Arguments

When a function registered as a Decorator is called by Handlebars, a set of arguments are passed to it. We’ll examine each of them in the following sections, so you can understand what you’re able to decorate with Decorators.

Here’s the complete function signature for a Decorator function:

function(program, props, container, context)

Decorator function return value

Decorators must return a function or falsy value (undefined, null, false, and so on). Any string or object returned will throw an exception. The function returned will be used to render the finished Handlebars string. If undefined is returned, the original program argument will be used implicitly.

program

This is the compiled Handlebars function that is passed data, and returns a rendered string. You can modify the arguments, the return value, or adjust the context when the function is called. Return this program argument to let the rendering pass through the Decorator. You can also “overwrite” the program argument by returning a different function.

props

Any properties set on this object will be set on the program function even if the program function is replaced. This is a safe place to set metadata that you want to access in other Decorators or helpers.

container

This is the current Handlebars runtime container. This has all the partials, helpers, and context data and can be modified (as you’ll see in the example below).

context

This is the parent context of your template, which includes any arguments to the Decorator as well as the data that was passed into the program function.

Formatting Money in Handlebars Pre-4.0

To demonstrate Decorators in the real world, let’s take a look at a template use case you may be familiar with: formatting money. We want to find a simple way to dynamically format a given value for a given currency. Handlebars does provide some existing mechanisms to solve this. Let’s look at a way to solve this problem with pre-4.0 Handlebars features.

First we create the helper to format money. The helper will accept the value to format and the currency as arguments:

//someFile.js
Handlebars.registerHelper('formatMoneyHelper', function(value, currency) {
    switch(currency) {
        case 'USD':
            return new Handlebars.safeString('$' + value + 'USD');
        case 'EUR':
            return new Handlebars.safeString('€' + value + 'EUR');
    }
});

Now we can use this helper in a template.

//template.hbs

Starting amount: {{formatMoneyHelper this.start this.format}}
Ending amount: {{formatMoneyHelper this.end this.format}}
Profit/Loss: {{formatMoneyHelper this.net this.format}}

We would expect our data to be in this format:

{
    start: 12.30,
    end: 15.30,
    net: 3.00,
    format: 'USD'
}

This isn’t a bad way to solve this. Helpers are designed for this kind of problems, but there is a lot of redundant code being written, both in the template and the helper. We might make more optimizations with this, but let’s examine the way to accomplish this task using Decorators in Handlebars 4.0 instead.

Formatting Money with Decorators in Handlebars 4.0

A better way to format money is to have a simpler helper that just takes the value. It should already understand what currency money should be formatted in. To do this in a dynamic way would be tricky with helpers, so let’s make use of Decorators to find a simpler solution.

Since Decorators are able to modify the main program function, let’s create a Decorator to set up a format helper function that will already have the currency loaded in. We’ll start with the JavaScript and Decorator registration.

function formatUSD(value) {
    return new Handlebars.safeString('$' + value + 'USD');
}

function formatEUR(value) {
    return new Handlebars.safeString('€' + value + 'EUR');
}

Handlebars.registerDecorator('activateFormatter', function(program, props, container, context) {
    var moneyHelper,
        format = context.args[0] || context.data.root.format;

    switch(format) {
        case "USD":
            moneyHelper = formatUSD;
            break;
        case "EUR":
            moneyHelper = formatEUR;
            break;
        default:
            console.log('Money format not set. Please set before rendering template.');
            moneyHelper = function() {};
    }

    container.helpers = {
        formatMoneyHelper: moneyHelper
    };
});

The Decorator takes care of registering the right formatting helper based on a static value or a format property in our context object, allowing it to be dynamic in loops as well. This makes our helper function to be much more modular and extensible. A side benefit about this approach is the testability of the formatting functions, since they are regular JavaScript.

Next let’s see how we can use this Decorator in our template:

//template.hbs

{{* activateFormatter}}

Starting amount: {{formatMoneyHelper this.start}}
Ending amount: {{formatMoneyHelper this.end}}
Profit/Loss: {{formatMoneyHelper this.net}}

This will use the format property in our context object to set the formatUSD function as our formatMoneyHelper helper function. We can override it as well, using this syntax:

{{* activateFormatter "EUR"}}

The implementation using Decorators is more elegant, testable, and allows you to control the formatting of the current block inside your template.

Decorators are incredibly powerful, and the above example is just a peek at what can be accomplished.

Conclusions

Hopefully this article has inspired you to use Inline Partials and Decorators in your own projects employing Handlebars. You’ve seen how Inline Partials are useful to define partials in your template and reduce the amount of JavaScript overhead to register partials. Moreover, you’ve seen that they’re perfect for small, repeating pieces of markup. On the other hand, Decorators allow you to modify the existing Handlebars block program function and give you control over the Handlebars runtime before execution. They’re perfect to mess with context data or helpers.

Therefore both Inline Partials and Decorators are powerful additions to an already essential tool for front-end development.

Now go forth and Decorate your Handlebars templates with Inline Partials!