Using Handlebars for your templates

In order to add dynamic data in our views, we needed a templating engine. And Handlebars is the templating engine we chose. In this document, we’ll explain how to use it, and how to use some helpers that can be very helpful for certain tasks.

How to use it

Let’s start with an easy example. We want to display a banner displaying Hello Georges! whenever the User georges logs in. The HTML to do that could be something like this:

hello.html

<div class="banner-hello">
  <span>Hello Georges!</span>
</div>

This hello.html file is a template. In order to add it somewhere in your UI, you would do container.append(monster.template(self, 'hello')) and it would add this HTML inside the container.

Now let’s add some dynamic data, since that’s why we need to use Handlebars in the first place! The problem with our template above, is that we need to say Hello to the user, and we don’t want to display Hello Georges! but Hello followed by the first name of the user. In order to do so, we need to have a variable inside this template. We know that in order to give a variable to our template we need to do:

app.js

var dataTemplate = {
    firstName: 'Pedro'
  },
  template = monster.template(self, 'hello', dataTemplate);

hello.html

<div class="banner-hello">
  <span>Hello {{firstName}}!</span>
</div>

It’s almost exactly the same template as above, except that we’re now using the firstName variable, and we can now display it properly for all the users.

Going further

We require all the templates to be internationalizable. We don’t force you to create more than the en-US.json file, but it’s important to have the capability to add more languages later on. If you want a better understanding on how to use the i18n, please go visit this link

Listing of different helpers

Handlebars comes with a lot of helpers already built, you can see the list here. It includes basic helpers such as each, if, unless, etc…

Now we’ll check which helpers we created to help us specifically with some common problems we encountered.

coalesce

This helper will return the first non nullish (null or undefined) element to be found from a list of 2 or more values. If all of them are nullish, then it will return null.

{{coalesce val1 val2 ... valN}}

compare

There is no way natively in Handlebars to compare if a string is equals to “test2” for example. Although it makes sense to try and avoid doing logic in the views, there are some places where we need this logic in the view instead of adding tons of code in the JS. The compare helper is here to help us with it.

In order to use it, you need to give 3 arguments:

  • the left value,
  • the operator, needs to be in this list: [’==’, ’===’, ’!=’, ’!==’, ’<’, ’>’, ’<=’, ’>=’, ‘typeof’],
  • the right value

Let’s see an example:

example.html

{{#compare role "===" "admin"}}
  <div class="admin-welcome">You're an admin! You must be so cool!</div>
{{else}}
  <div class="peon-welcome">You're a user! That's cool.</div>
{{/compare}}

In the above example, we check if the role of a user is "admin" and if it is we display a special div, otherwise we display another one. We can use it the same way to compare values, for example {{compare moneyLeft ">=" 10000}}You're so rich...{{/compare}}. You get the idea!

debug

The debug helper is really helpful to debug your templates. Sometimes you lose track of the current context in the template, and dropping a {{debug}} will output the current context in the console. You can also add an argument after it to output a specific object. Here’s an example:

example.html

{{debug}} <!-- Would output the entire context, containing users and anything else -->
{{debug users}} <!-- Would output the entire users object}} -->
{{#each users}}
  {{debug}} <!-- Would output the object representing the current user -->
  <div class="user-div">
    <span class="user-first-name">{{firstName}}</span>
    <span class="user-last-name">{{lastName}}</span>
  </div>
{{/each}}

formatMacAddress

The formatMacAddress helper formats a string into a string representation of a MAC address, using colons as separator.

{{formatMacAddress 'E3.FF.34.9A.71.BC'}}

formatPhoneNumber

The formatPhoneNumber helper can display a phone-number with proper formatting automatically. It’s useful because a lot of numbers are stored in the database with a format such as +14151993821 and we want to display them to the user like +1 (415) 199-3821.

example.html

<div class="user-div">
  <span class="user-first-name">{{firstName}}</span>
  <span class="user-last-name">{{lastName}}</span>

  <div class="user-phone-number">{{formatPhoneNumber phoneNumber}}</div>
</div>

formatPrice

The formatPrice helper can display a price with the proper amount of digits after the decimal point as well as the currency used by the UI. It’s useful because prices can have a various number of digits, but should be displayed in a consistent way. The example below will round off the price to two (2) digits, and display it with two (2) digits even if it has less than that (i.e. “6” -> “6.00”, “6.666” -> “6.67”).

<div class="item-row">
  <span class="item-name">{{name}}</span>
  <span class="item-price">{{formatPrice price 2}}</span>
</div>

If you do not want the currency to be displayed, you’ll have to specify a third argument:

<div class="item-row">
  <span class="item-name">{{name}}</span>
  <span class="item-price">{{formatPrice price 2 false}}</span>
</div>

getUserFullName

This helper will display the full name of the user object passed as argument, following the internationalization rules that are currently active. If no argument is provided, it will return the full name of the user that is currently logged in. The user parameter should be a plain object that has the properties first_name and last_name.

{{getUserFullName optionalUserObject}}

ifInArray

This helper will check if an array contains a specific value and display the related HTML.

example.html

<input type="checkbox"{{#ifInArray 'VP8' media.video.codecs}} checked="checked"{{/ifInArray}} value="{{codec}}"/>

Here we tick the checkbox depending on the ‘VP8’ video codec being in the list of available codecs.

isPrivLevelAdmin

This is a predetermined conditional helper letting you render content only if the privilege level of the user logged in or passed as argument is admin.

{{#isPrivLevelAdmin optionalUserObject}}
...
{{/isPrivLevelAdmin}}

isSuperDuper

This is a predetermined conditional helper letting you render content only if the account logged in or passed as argument has the superduper_admin flag set to true.

{{#isSuperDuper optionalAccountObject}}
...
{{/isSuperDuper}}

languageSelector

This inline helper displays a select list of the languages supported by Monster UI.

{{languageSelector}}

You can specify the value of the selected language, and whether or not to display a “Default” option, via the selectedLanguage and showDefault options.

{{languageSelector selectedLanguage="en-US" showDefault="true"}}

You can provide HTML attributes as well:

{{languageSelector name="user_language" class="list-selector" multiple=""}}

lookupPath

This helper allows to get a value from a specific path of an object. Optionally a defaultValue can be passed as third parameter, and it will be returned if the resolved value is undefined.

{{lookupPath object path defaultValue}}

Let’s say we have an object like:

var basket = {
  fruits: {
    apples: 5,
    pears: 3,
    bananas: 1
  },
  vegetables: {
    carrots: 5
  }
}

Then, in the HTML template you could do something like this:

The basket has {{lookupPath basket "fruits.apples" "0"}} apples and {{lookupPath basket "vegetables.potatoes" "0"}} potatoes.

The final output would be something like:

The basket has 5 apples and 0 potatoes.

monsterCheckbox

This helper allows you to generate a pretty checkbox from a simple checkbox input.

{{#monsterCheckbox}}
  <input type="checkbox" />
{{/monsterCheckbox}}

Any property (class, id, checked, data-something ,etc…) set on the input will be conserved. You can also provide parameters to define a label, set its positioning, and set the size of the checkbox. All optional.

{{#monsterCheckbox "large-checkbox" "prepend-label" "My Checkbox's label"}}
  <input type="checkbox" />
{{/monsterCheckbox}}

monsterNumberWrapper

This helper will generate a wrapper around a phone number that will automatically format that number based on user preferences, as well as showing a flag if the phone number was a valid phone number from a known country.

example.html

{{#monsterNumberWrapper this.phoneNumber}}{{/monsterNumberWrapper}}

monsterPanelText

This helper is a re-design of the monsterText wrapper. It allows developer to display “informational” messages nicely.

There are a few parameters available to customize this helper.

First one is the title, this new panel includes a section for a title. You should use a short text like “Warning!” or “Information”.

The second parameter is the style. There are 4 different styles for this panel, info (blue), success (green), warning (yellow), dange” (red).

Finally you can add a className as a third parameter. This is helpful if you need to customize the helper yourself. You can also use fill-width as a class name to automatically fill the parent’s div width with the helper. Without that it will be set to a fixed size.

Here are some examples on how to use this helper.

{{#monsterPanelText 'Warning!' 'warning' 'fill-width'}}
  You should see a warning message here!
{{/monsterPanelText}}
{{#monsterPanelText 'Help' 'info'}}
  <p>This is an informational message</p>
{{/monsterPanelText}}
{{#monsterPanelText 'Error!' 'danger' 'fill-width'}}
  You should see an error message here
{{/monsterPanelText}}
{{#monsterPanelText 'Congratulations!' 'success' 'fill-width'}}
  You should see a success message here
{{/monsterPanelText}}

monsterRadio

Similar to monsterCheckbox, this helper allows you to generate a pretty radio button from a simple radio input.

{{#monsterRadio}}
  <input type="radio" name="myRadioName" />
{{/monsterRadio}}

Any property (class, id, checked, data-something ,etc…) set on the input will be conserved. You can also provide parameters to define a label, set its positioning, and set the size of the radio. All optional.

{{#monsterRadio "large-radio" "prepend-label" "My Radio Button's label"}}
  <input type="radio" name="myRadioName" />
{{/monsterRadio}}

monsterSignalIndicator

This helper will generate a signal strength indicator using a pair of parameters to indicate the strength and type of signal to display.

{{#monsterSignalIndicator strength}}
  {{label}}
{{/monsterSignalIndicator}}
  • strength: Number from 0 to 4
  • label: String

monsterSwitch

This helper allows you to generate a switch from a simple checkbox.

{{#monsterSwitch}}
  <input type="checkbox" />
{{/monsterSwitch}}

Any property (class, id, checked, data-something ,etc…) set on the input will be conserved.

monsterText (obsolete, use monsterPanelText where possible)

This helper allows you to generate a wrapper around your text, with a pre-set design used in many different places in the Monster-UI, so it looks consistent with the rest of the UI.

First argument is optional and defaults to ‘info’. Choices are ‘info’, ‘question’, ‘error’, ‘warning’. Error will display a wrapper of the red color, Warning will display a wrapper of the yellow color, and question will show a question mark instead of the standard info icon.

Second argument is optional and let you define a className that will be added to the main wrapper so you can apply some CSS rules to it.

{{#monsterText}} Monster is <h3>awesome</h3> {{/monsterText}}
{{#monsterText 'error'}} Monster is <strong>NOT</strong> awesome {{/monsterText}}
{{#monsterText 'warning' 'myClassName'}} Monster could be awesome {{/monsterText}}
{{#monsterText 'question'}} Is Monster Awesome?{{/monsterText}}

replaceVar

This helper allows you to replace a variable inside another variable with Handlebars. It’s useful for i18n keys. You can see a very good example about this helper here.

svgIcon

This helper makes it easy to render SVG icons from collections supported by Monster UI by only requiring you to specify an icon ID.

{{svgIcon "telicon2--phone-outbound"}}

Optionally, additional HTML attributes can be provided to further customize the SVG tag.

{{svgIcon "g-drive--color" class="my-icon-class icon-large" data-tooltip="Click here" disabled="true"}}

telicon

This helper is a wrapper over svgIcon and allows you to render telicon icons by specifying an icon ID stripped of the telicon2 prefix.

{{telicon "phone-outbound"}}

Like svgIcon, additional HTML attributes can be provided to the SVG tag.

{{telicon "phone-outbound" style="display:block;" data-label="Click here"}}

toFriendlyDate

This helper allows you to display time in a customizable format. It take a mandatory Gregorian timestamp in parameter and returns a formatted date:

{{toFriendlyDate gregorianTimestamp}}

Output: 12/01/14 12:43PM

If you want to display a time in another format, you can specify an optional parameter:

{{toFriendlyDate gregorianTimestamp 'DD-MM-year hh:mm:ss12h'}}

Output: 12-01-2014 12:43:23PM

In bonus, you can set the optional parameter to short to simply display the date:

{{tofriendlyDate gregorianTimestamp 'short'}}

Output: 12/01/2014

This helper will search for the following strings and replace them by the corresponding values:

  • year: full year
  • YY: last 2 digits of the year
  • month: month of the year in letters
  • MM: month as a 2 digits number
  • day: day of the week in letters
  • DD: date of the day
  • hh: hours
  • mm: minutes
  • ss: seconds
  • 12h: use the 12h format (if not specified, the 24h format is used)

toLowerCase

This helper is pretty simple, and is basically executing the toLowerCase() JS function, to a string in a template. For example it would transform Giorgio in giorgio.

example.html

<div class="user-div">
  <span class="small-user-first-name">{{toLowerCase firstName}}</span>
</div>

tryI18n

If you want to display data from the back-end, often times you’ll try to parse the text and have a better text displayed instead. But what if the back-end moves faster and add more keys? You still want to be able to display text for that key. This is what tryI18n is for. Basically, it will check if a key has a translation in a JSON object. If it has one, it will display the friendly text, if not, it will display the variable name!

Let’s say we have a i18n file like:

{
  "demoHandlebars": {
    "test_1": "Test 1",
    "test_2": "Test 2",
    "test_3": "Test 3"
  }
}

and a HTML template named ‘templateName’ like:

{{#each keys}}
  {{ tryI18n i18n.demoHandlebars this }}<br/>
{{/each}}

And we use this in JS:

var obj = { keys: ['test_1','test_2','test_3'] };

monster.template(self, 'templateName', obj);

The final output would be something like

Test 1

Test 2

Test 3

On this page