Handlebars, JavaScript, Node

Handlebars templating language allows you to add 'helpers' that execute JavaScript during the template execution - Allows for advanced processing of the data.

One of the main reason I've opted to use handlebars is the ablity to add logic to the execution of the templates, here's a little example of how to create special helpers.

Rendering Consistant Decimal values

Lets start with a simple example of taking a JavaScript number, that contains the price, that we want to format into an easy to read format.

Given the following data :

var data = {
  total:3100.1,
  tax:112,
  delivery:0
}

Very easy to render these with :

<p>Total : {{total}}</p>
<p>Tax : {{tax}}</p>
<p>Delivery : {{delivery}}</p>

Giving :

<p>Total : 3100.1</p>
<p>Tax : 112</p>
<p>Delivery : 0</p>

While the content is correct the formatting of the numerical values is awful.

You could modify the application generating the JSON to render pre-formatted strings, but IMHO it's better to use proper values and let the presentation layer control the formatting of the data.

So you need to use Handlebars.registerHelper to create & register a helper to format during the template execution, for example:

// dec - returns a 2 decimal placed value #.##
Handlebars.registerHelper('dec', function (rawValue, options) {
  var val = (
    Math.round(
      (+rawValue) * 100
    ) / 100
  )
  .format(2);

  return val;
});

This code registers a helper named dec that converts the value passed to a numeric before formatting to 2 decimal places.

Please Note the conversion is using the old skool * 100 & / 100 factoring method for rounding. To see more detalis about why this is required see the EFnet #JavaScript IRC FAQ

Andrew Scott

Here's how you would use it :

<p>Total : {{dec total}}</p>
<p>Tax : {{dec tax}}</p>
<p>Delivery : {{dec delivery}}</p>

Giving :

<p>Total : 3100.10</p>
<p>Tax : 112.00</p>
<p>Delivery : 0.00</p>    

Which renders in a much better & consistant way.

Advanced Conditional Block Helpers

One gripe I have with Handlebars is conditionals. for example there's no built in way to test if ( foo == bar ) or if( foo > bar ); the native #if block helper is only a 'falsy' test. Which means you have to either add a bunch of fasly values or a bunch of special conditions block helpers.

That is unless you create/use a generic block helper...

A nice post by the dog in the hat, back in 2012, on Comparison Block Helpers has a working example. However, there's a better implementation in the comments that I've included (slightly modified) below :

Handlebars.registerHelper('compare', function (lVal, op, rVal, options) {

  // Check Required arguments
  if (arguments.length < 3) {
    throw new Error(
      'Handlerbars Helper "compare" needs at least 2 parameters'
    );
  }

  // Set Defaults & handle shorthand conditional
  if (options === undefined) {
    options = rVal;
    rVal = op;
    op = "===";
  }

  // Define the comparison functions
  var ops = {
    '==':     function (l, r) { return        l ==  r; },
    '===':    function (l, r) { return        l === r; },
    '!=':     function (l, r) { return        l !=  r; },
    '!==':    function (l, r) { return        l !== r; },
    '<':      function (l, r) { return        l <   r; },
    '>':      function (l, r) { return        l >   r; },
    '<=':     function (l, r) { return        l <=  r; },
    '>=':     function (l, r) { return        l >=  r; },
    'typeof': function (l, r) { return typeof l ==  r; }
  };

  // Validate requested comparison
  if (!ops[op]) {
    throw new Error(
      'Handlerbars Helper "compare" doesn\'t know the operator: ' + op
    );
  }

  // Execute comparer, return conditional fragment
  return ops[op](lVal, rVal)
    ? options.fn(this)
    : options.inverse(this);

});

The code is relative self explanatory:

  1. Checks the no. of params
  2. Handles the defaults
  3. Defines a key'd set of comparers
  4. Check for the validity of the requested comparision
  5. Executes the comparison and returns the results

Some people said the more complex parts are 2 & 5 & 3 contains the main body of the helper, so I'll clarify them below, but first we should look at how it's used.

Using {{#compare ... }}

This allows conditionals to be written in a more legible & expected format, taking the previous example to check Tax & Delivery:

<p>Total : {{dec total}}</p>

{{#compare tax ">" 0}}
  <p>Tax : {{dec tax}}</p>
{{/compare}}

<p>
  Delivery : 
  {{#compare delivery ">" 0}}
    {{dec delivery}}
  {{else}}
    Free
  {{/compare}}
</p>

Giving (whitespace omitted for brevity) :

<p>Total : 3100.10</p>
<p>Tax : 112.00</p>
<p>Delivery : Free</p>    

Key'd Set of Conditionals

The main body of the helper is the key'd set of conditional functions :

// Define the comparison functions
var ops = {
  '==':     function (l, r) { return        l ==  r; },
  '===':    function (l, r) { return        l === r; },
  '!=':     function (l, r) { return        l !=  r; },
  '!==':    function (l, r) { return        l !== r; },
  '<':      function (l, r) { return        l <   r; },
  '>':      function (l, r) { return        l >   r; },
  '<=':     function (l, r) { return        l <=  r; },
  '>=':     function (l, r) { return        l >=  r; },
  'typeof': function (l, r) { return typeof l ==  r; }
};

Above, JavaScript Object Literal notiation has been used to define a object named ops that has properties who's keys are strings of the opperator. This allows the function for, say, 'equals' to be referenced via ops['=='].

We could have easily used individual named functions for example :

function equals(l ,r)      { return l ==  r; }
function exactEquals(l ,r) { return l === r; }      
function notquals(l ,r)    { return l !=  r; }      
...

But not only is this aproach less legiable, it also requires some switch or map to relate opperators ==, ===, != to the named functions equals, exactEquals & notEquals respectivly.

Defaults & Shorthand Notation Used by {{#compare ... }}

The defauls & shorthand notation are handled in this peice of JavaScript:

if (options === undefined) {
  options = rVal;
  rVal = op;
  op = "===";
}

First thing to note is the execution {{#compare L_VALUE ["OPPERATOR"] R_VALUE}}.

The "OPPERATOR" param is optional allowing the shorthand statement {{#compare L_VALUE R_VALUE}} that tests the default exactaly equals, this being catered for via the portion JavaScript above.

Clearly this script checks for the missing options argument and swaps about the other arguments; placing the 2nd argument op into rVal, 3rd value rVal into options and defaulting the operation to ===.

Which means the Delivery line could have been written (ommiting the fact I really want to test less than - just wanted a quick n' dirty example!):

<p>
  Delivery : 
  {{#compare delivery 0}}
    Free
  {{else}}
    {{dec delivery}}
  {{#compare}}
</p>

Personally, I prefer to include the opperator, it's just more legiable!

Andrew Scott

Executing {{#compare ... }} Comparison & Results

The execution of the comparison & returning the results are handled in a terniary return statement that executes the comparison:

return ops[op](lVal, rVal)
  ? options.fn(this)
  : options.inverse(this);

To make it easier to understand I'll break the both the execution of the comparisson & the terniary statement in to seperate statements below.

Executing the Comparison Function

The ops object contains a key'd set of functions all reciving lVal & rVal, that return a boolean result based on the params & comparison. The key used for this set is the comparer it self: ==; >; etc. as defined in the variable op.

Given a key with the value > the function returned from the key'd set of opperators (ops[op] or ops['>']) would be :

function (l, r) { return l > r;  }

Expanding the ops[op](lVal, rVal) of the terniary return statement would result in something similar to :

// Get the comparison function
var compFn = ops[op];    

// Call the comparison function, passing lVal & rVal 
var result = compFn(lVal, rVal); 

Now result contains a boolean indicating the outcome of the comparison.

Returning the Relevant Block/Fragment of the Templates

For the most part the {{#compare ... }} helper is the same as the Handlebars native {{#if ... }} block helper.

As seen on the block helpers page there are 2 functions provided by handlebars:

Handlebars provides the block for the else fragment as options.inverse

Yehuda Katz (Handlebars author)

  • options.fn - Used to execute the true fragment of the template

  • options.inverse - Used to execute the {{else}} fragment

The functions execute against the relevant true/false fragments of the block within the template and need to be called.

Continuing with the expansion started above from:

return ops[op](lVal, rVal) 
  ? options.fn(this)
  : options.inverse(this);

Now the variable result contains a boolean that is the resulatant call of ops[op](lVal, rVal) the return can be written :

if( result ){
  return options.fn(this);
}else{
  return options.inverse(this);
}

Hopefully this example shows you the flexibility that can be achived via Handlebars' block helpers methods, and why I have a preference for it over most other templating toolsets.

Flexible Date Formatting

Before I show you the formatter for Dates we need to make sure we have valid date object to work with. If you already have valid date skip to the next part Actual Date Formatting.

It's Important to Get a Valid Date obj

UPDATE : Checkout out moment.js a powerfull JavaScript Date / Time Lib. Also they appear to cater for these odities with ASP.net JSON Dates a'la :

Here's the regexp they use to handle the JSON format, might prove better than my example.

// ASP.NET json date format regex 
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,'

Depending on the libs used to generate the JSON some date objects are rendered differently, typically they are of the format :

"date": "2013-09-17T09:44:14.253"

Or

"date": "2014-07-28T08:38:00.68+01:00"

I have seen some that use the literal with ticks:

"date": new Date(1406544772516)

Regardless of the format that the JSON is rendered in you can create a proper date object.

The Date Format Plugin supports a number of different input formats, so you might find it's best use it's parsers over these example below.

Andrew Scott

To test which type an isString function, like this, can be used :

function isString(obj) {
  return toString.call(obj) == '[object String]';
}

To parse the string value of a date you could use:

function parseStringDate(rawValue) {
  if (isString(rawValue)) {
    return (new Date(rawValue));
  }
  return '';
}

To parse the ticks if the value is a literal Date :

function parseLiteralDate(rawValue) {
  if (typeof rawValue.match == 'function') {
    return (new Date(
      +rawValue.match(/[\d]+/)
    ));
  }
  return '';
}

Here the regexp /[\d]+/ matches out the ticks and the results are passed through the new Date() initializer.

The Actual Date Formatting

Assuming we have a valid date object we can use other libs to format the date object. A great tool is the jQuery plugin date format plugin, the rest of this example assumes you have the plugin avaliable to your Handlebars Execution (it's simple enough todo, just include it & jQuery at runtime).

Handlebars.registerHelper('formatDate', function (rawValue, options) {

  // Exract Format from options, default to dd/MM/yyyy
  var fmt = options.hash.format === undefined 
    ? 'dd/MM/yyyy' 
    : options.hash.format;

  // Replace with respective date parser 
  var dDate = PARSE_DATE_FN(rawValue);

  if (!isValidDate(dDate)) {
    return '';
  }

  return $.format.date(
    dDate,
    fmt
  );

});

This allows you to parse any format to the helper, that the plugin supports, via the options hash.

For example :

<p>{{formatDate date}}</p>
<p>{{formatDate date format="ddd D MMMM yyyy H:mma"}}</p>
<p>{{formatDate date format="E d MMM"}}</p>
<p>{{formatDate date format="HH:mm:ss:SSS"}}</p>

Which results :

<p>26/07/2014</p>
<p>Saturday 26th July 2014 9:45AM</p>
<p>Sat 26 Jul</p>
<p>09:45:34:123</p>

As you can see there's lots of flexibility available for formatting date objects.

A Useful Little {{timeStamp}} & Reference Helper

Here's a useful timeStamp helper I use to log the date/time the template was generated, great for PDFs that contain time sensitive information.

Handlebars.registerHelper('timeStamp', function (rawValue, options) {

  rawValue = rawValue === undefined ? '' : rawValue;

  return $.format.date(
    (new Date()),
      'dd/MM/yyyy HH:mm:ss'
    ) + (
      // Add the Ref
      (rawValue == '') ? '' : ' [' + rawValue + ']'
  );
});

It can be used like this:

<time>{{timeStamp}}</time>
<time>{{timeStamp "AC/01811/11"}}</time>

Rendering :

<time>26/07/2014 11:23:06</time>
<time>26/07/2014 11:23:06 [AC/01811/11]</time>

I use these in the footers of all pages, along with QR Codes, to help enforce versioning of customer specific generated PDFs. The in-house staff can visually check the published time against the tracked copy, ensure the latest version is being used.