The Handlebars JavaScript templating engine provide a single if-conditional with an optional else, but that if-statement can only handle a single value, not an expression. You can write

{{#if name}}
..
{{/if}}

but you cannot write

{{#if name == 'Foo'}}
..
{{/if}}

Let's create a Handlebars helper that will provide this functionality.

if conditional

Before creating the helper though, let's see a full example using the plain if statement. There are two values in the data object: cond1 and cond2, true and false respectively. The rest of the JavaScript code is just fetching the template and letting Handlebars process the data.

examples/js/handlebars_if.js

var data = {
   'cond1'  : true,
   'cond2'  : false,
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

The template itself has two entries like this using each one of the variables:

{{#if cond1}}
    true
{{else}}
    false
{{/if}}

The full html code is this:

examples/js/handlebars_if.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if cond1 (expected true)
    {{#if cond1}}
       true
    {{else}}
      false
    {{/if}}

    <br>#if cond2 (expected false)
    {{#if cond2}}
      true
    {{else}}
      false
    {{/if}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>


Try!

You can try it by clicking on the Try link. In the new page the "show" button will trigger the process.

if_eq

In the next example we have implemented a Handlebars helper called if_eq. It expects two parameters and will compare them using ==. The helper itself looks like this:

Handlebars.registerHelper('if_eq', function(a, b, opts) {
    if (a == b) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});

The template using this looks like this: (name is an attribute passed to the template function.)

{{#if_eq name 'Foo'}}
      true
{{else}}
      false
{{/if_eq}}

The full JavaScript file also contains the data object and the code we had earlier combining the template with the data:

examples/js/handlebars_helpers_if_eq.js

var data = {
   'name'   : 'Foo',
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

Handlebars.registerHelper('if_eq', function(a, b, opts) {
    if (a == b) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});

The full HTML file including the template looks like this:

examples/js/handlebars_helpers_if_eq.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if_eq name Foo (expected true)
    {{#if_eq name 'Foo'}}
      true
    {{else}}
      false
    {{/if_eq}}
    
    <br>#if_eq name 'Bar' (expected false)
    {{#if_eq name 'Bar'}}
       true
    {{else}}
       false
    {{/if_eq}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>

Try!

You can try it after clicking on the "Try" link.

Uncaught Error: if_eq doesn't match if - 3:7

When I encountered this error it took me quite a while to figure out what went wrong. It might have been just something blocking my mind, I am not sure. Can you spot the problem in the next example:

examples/js/handlebars_helpers_if_eq_typo.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#if_eq name Foo (expected true)
    {{#if_eq name 'Foo'}}
      true
    {{else}}
      false
    {{/if}}
    
    <br>#if_eq name 'Bar' (expected false)
    {{#if_eq name 'Bar'}}
       true
    {{else}}
       false
    {{/if_eq}}
  </script>

</head>
<body>
<button id="show">Show</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_helpers_if_eq.js"></script>

</body>
</html>

Try!

This happened when I started to convert my if conditional to an if_eq conditional, but I only changed the opening expression from {{#if ...}} to {{#if_eq ...}} but not the closing expression that was left as {{/if}}. Hence the error telling us that if_eq does not match if. Maybe if the error message had the keywords stand out, it would have been easier.

Anyway, look out for such typos. They are a waste of time. Make some more interesting bugs!

iff - for other conditionals

Finally we got to build the more generic helper for conditional expressions. I called it iff. I am aware of the mathematical meaning of it, but it just looked cute and short to be used for a generic comparision helper. The idea is that I'd like to be able to write expressions like these:

    {{#iff name '==' 'Foo'}}

and like this:

    {{#iff answer '>' 40}}

In the sample I've created two templates. In the first template I used there are 3 such conditionals. In the second template there is a single conditional: {{#iff 4 '*' 5}} that I included just to show what will happen if we supply an operator that is not supported by the iff helper. I also added two buttons, one to process and show the first template and one to process and show the second template.

examples/js/handlebars_conditionals.html

<html>
<head>
  <title>Handlebars conditionals</title>

  <script id="text-template" type="text/x-handlebars-template">
    <br>#iff name '==' 'Foo' (expected true)
    {{#iff name '==' 'Foo'}}
      true
    {{else}}
      false
    {{/iff}}

    <br>#iff 42 &gt; 40 (expected true)
    {{#iff answer '>' 40}}
      true
    {{else}}
      false
    {{/iff}}

    <br>#iff 42 &gt; 50 (expected false)
    {{#iff answer '>' 50}}
      true
    {{else}}
      false
    {{/iff}}
  </script>

  <script id="text2-template" type="text/x-handlebars-template">
    <br>Exception!
    {{#iff 4 '*' 5}}
       true
    {{else}}
       false
    {{/iff}}
  </script>

</head>
<body>
<button id="show">Show</button>
<button id="show2">Try invalid</button>
<hr>
<div id="content"></div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.min.js"></script>
<script src="/try/examples/js/handlebars_conditionals.js"></script>

</body>
</html>

Try!

The JavaScript file has the data, the code reacting to clicks and processing the templates and the the partial(!) implementation of the iff helper. Basically it is just a giant switch statement with a separate case for each valid operator. The default behavior, when the given operator is not handled by any of the case statements, is to throw an exception.

examples/js/handlebars_conditionals.js

var data = {
   'cond1'  : true,
   'cond2'  : false,
   'name'   : 'Foo',
   'answer' : 42
};

document.getElementById('show').addEventListener('click', function () {
    var source = document.getElementById('text-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});

document.getElementById('show2').addEventListener('click', function () {
    document.getElementById('content').innerHTML = 'Look at the console!';
    var source = document.getElementById('text2-template').innerHTML;
    var template = Handlebars.compile(source);
    var html = template(data);
    document.getElementById('content').innerHTML = html;
});


Handlebars.registerHelper('iff', function(a, operator, b, opts) {
    var bool = false;
    switch(operator) {
       case '==':
           bool = a == b;
           break;
       case '>':
           bool = a > b;
           break;
       case '<':
           bool = a < b;
           break;
       default:
           throw "Unknown operator " + operator;
    }

    if (bool) {
        return opts.fn(this);
    } else {
        return opts.inverse(this);
    }
});


#compare

After reaching this point I found out that there is already an implementation of such a helper called #compare. It can be found among the comparison helpers.

In any case I think it was interesting to see how to build and use this.

Comments

Handlebars is pretty useless, how difficult can it be to include support for expressions? That you have to declare so much code for this kind of basic behaviour...


Thank you so much for this! Spent ages looking online for an example of 'if_eq' and yours is the first one that clicked for me.


Thank you for the Uncaught Error section, saved me several hours :)


Thanks for this great post! But for the first example, where true is expected false is returned. please verify and correct it. I ma facing the same issue in my project.