Working with Underscore JavaScript Templates

0
15817
underscore

underscore

This article is targeted at front-end developers and takes the reader on a tour of Underscore.js, which is an easy-to-learn JavaScript library.

Web based applications are very popular due to cross-platform support. They also don’t need any software to be installed on the client side due to the versatility of the browsers.
A decade ago, we had limitations on client-side processing power. So we set up high-end servers, produced most of the HTML content on the server side and sent it to the client. We kept all the logic including HTML generation on the server side. On the client side, we just used JavaScript to validate the user’s inputs.

Things changed over a period of years. The processing power of the client increased rapidly. At the same time, the amount of client-side code also increased. In other words, part of the processing was moved from the server to the client.
Now, it is common to see JS files with more than 1000 lines of code. A larger number of lines obviously increases complexity. To keep the code simple and readable, there is need for a structure or design patterns. We now see many frameworks doing this job. For example, BackboneJS, AngularJS and EmberJS are very popular JS frameworks.

In client-side programming, separating the view generation part is most important in order to reduce code complexity and improve code readability. Many template engines are available to handle the view part, e.g., Underscore.js, Handlebars.js, Jade.js, dust.js, Mustache.js,…

Among these JS templates, Underscore.js is very simple and easy to learn.

Figure 1

Underscore.js
Underscore.js is a JavaScript library. It has a number of useful functions to make life easy. This library was created by Jeremy Ashkenas. He is the creator of the CoffeeScript programming language, the Backbone.js JavaScript framework, and the Underscore.js JavaScript library. Currently, many developers are contributing to the Underscore.js library. It is open source and the latest available version is 1.8.3.
The site http://underscorejs.org provides the complete list of functions available with Underscore.js. The _.template is one of the functions that provides template features.
This library is a hard dependency for the BackboneJS framework. If you currently use the Backbone framework in your project, it means you already have Underscore.js and you can use its functions. So we can say that, by default, the Backbone framework uses Underscore as a template solution.

Installation
You can install Underscore by using a JavaScript package manager such as Bower, npm, Meteor or Component.js by the following methods:

Node.js

npm install underscore

Meteor.js

meteor add underscore

Bower

bower install underscore

Component.js

component install jashkenas/underscore

You can also download the JS file from the site http://underscorejs.org. In the following sample code, let’s download and use the Minified form of JS library.

Basic usage
There are three types of tags available on the Underscore template.
1. interpolate <%= … %>
This prints the contents of the variable or the result of the expression presented inside the tag.
2. evaluate <% … %>
This interprets the JavaScript statements available inside the tag, by which we can provide logical structure to the template.
3. escape <%- … %>
This is similar to interpolate but escapes the following characters: &, <, >, “, ‘ , and /
Let’s now look at how the above tags are used. I am using an Apache Web server and I keep the following HTML, JS and library files under the directory undertemp, which is under the webroot directory.
Figure 2 gives a very simple example that shows you the details of how to use a template.

Figure 2

index.html

1 <head>
2 <meta charset=”UTF-8”>
3 <title>Underscore Template sample</title>
4 </head>
5
6 <body>
7
8 <script type=”text/template” class=”students-list-template”>
9 <% _.each( listItems, function( listItem ){ %>
10 <li>
11 <%- listItem.name %>
12 <% if ( listItem.mark >30 ){
13 print(‘<em>Pass</em>’);
14 } else {
15 print(‘<em>Fail</em>’);
16 }%>
17 </li>
18 <% }); %>
19 </script>
20
21 <script src=”jquery-2.1.3.min.js”></script>
22 <script src=”underscore-min.js”></script>
23 <script src=”view.js”></script>
24 <header> </header>
25 <section> </section>
26 <div id=”students”> </div>
27 </body>
28 </html>

view.js

1 $( document ).ready(function() {
2
3 var tpl = _.template(“<h1>Hello <%= course %> students</h1>”);
4 var tplString = tpl({course: “MBA”});
5 $(“header”).html(tplString);
6
7 var stuData = {
8 listItems: [
9 {
10 name: “Kavi”,mark: 89
11 },
12 {
13 name: “Aarthi”,mark: 78
14 },
15 {
16 name: “Bala”,mark: 15
17 },
18 {
19 name: “Shrini”,mark: 45
20 }
21 ]
22 };
23
24 var tpl1 = _.template($(“.students-list-template”).html());
25 var tplString1 = tpl1(stuData);
26 $(“#students”).html(tplString1);
27 });

index.html: An explanation

  • We use the Jquery library for DOM manipulation. So the Jquery library has been included in Line 21.
  • Lines 8-19 define the template inside the script tag with type as text/template. JavaScript does not interpret this content. We can retrieve the content by using the following Jquery class based selector:
$(“.students-list-template”).html()

This template uses the escape and evaluate tags.

  • By using the escape tag <%- listItem.name %>, it prints the name after performing the _.escape function on it.
  • By using the evaluate tag <% _.each( listItems, function( listItem ){ %> , it iterates the given array of data. The evaluate tag helps to provide the logical structure to the template.

view.js: An explanation

  • Line 1 uses the Jquery ready statement, to start the interpretation after loading HTML.
  • Line 3 simply defines the template as a string; it is compiled by the statement _.template() and returns the function. This is called precompilation. This is explained in detail in the next section. This template uses the interpolate tag <%= course %>. It prints the contents of the course variable.
  • In Line 4, data is passed to the function tpl. This function replaces the variable with data and it returns the HTML content.
  • Line 5 inserts the HTML content to the <header> tag by using the Jquery function.
  • Lines 7-22 define the sample JSON data. In real-time, it might be fetched from the server side API by using an AJAX request.
  • Lines 24-26 also work in the same way as Lines 3–5, but it takes the template from the separate script tag by using the Jquery selector.

Template settings
We can use the following settings to change the default behaviour of the template engine:

_.templateSettings = {
evaluate : <regexp>,
interpolate : <regexp>,
escape : <regexp>,
variable : <variable name>
};

Changing the syntax
By default, Underscore uses the ERB style template delimiters. In some cases, there may be a need to change the syntax of the Underscore template to avoid a clash with server-side tags. In the above settings, the first three values helps to change the syntax of tags in the templates.
You can see the example given below:

_.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g,
evaluate : /<%([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g,
variable : “x”
};
var tpl = _.template(“<h1>Hello {{x.course}} students, year {{x.year}}</h1>”);
var tplString = tpl({course: “MBA”, year:2015});
$(“header”).html(tplString);

In the above example, Interpolate looks for the tags like {{ … }}. For the evaluate and escape tags, we keep the default Underscore template syntax. If you want to change syntax for a specific tag, keep the default values for the other tags like in the above example. Otherwise, it won’t recognise those elements.

Limiting the scope
The variable parameter helps to limit the scope of the data we pass into the template. Otherwise, the function created by precompilation uses the with statement to control the scope. The with statement is recognised as a source of confusing bugs and compatibility issues. It is important to set up this variable while configuring, to avoid the usage of the with statement. It makes the template faster.
We can also set it up as follows:

_.templateSettings.variable = “x”;

Precompilation
One of the important concepts of the template engine is precompilation. Basically, all template engines use some regular expressions to replace the tags with data. Since regular expressions are very costly in terms of processing, the templates are converted to functions and these functions can be used many times with less processing power. This is called precompilation.
We can see this precompiled template (function) by using Firebug (see Figure 4).
The following function is created by using Underscore for the template ‘Hello MBA students’.

var __t,
__p=’’,
__j=Array.prototype.join,
print=function(){
__p+=__j.call(arguments,’’);
};
with(obj||{}){
__p+=’<h1>Hello ‘+
((__t=( course ))==null?’’:__t)+
‘ students</h1>’;
}
return __p;

As I have stated, in template settings, this function uses the with statement to control the scope. If we use the variable parameter while configuring, this function will be generated without the with statement as below:

var __t,
__p=’’,
__j=Array.prototype.join,
print=function(){
__p+=__j.call(arguments,’’);
};

__p+=’<h1>Hello ‘+
((__t=( x.course ))==null?’’:__t)+
‘ students</h1>’;
return __p;

If you look closely at the above function, you will understand the basic behaviour of the Underscore template. This function creates a logical string concatenation to create the HTML content. It also defines the print function.

Figure 3

Figure 4

Modular development
In the above example, the template is kept in the same JavaScript file. To keep the code clean and modular, it can be kept in a separate file. There are many ways to separate the code.
1. Keep the template content as a string and assign it to a global variable. Save this as a .js file and include it by using the <script> tag. Use the global variable to access the template content. Make sure that the template file is included before the JS file where the template is accessed.
2. Keep the template content in a text file. Write a simple server-side script to read the file and echo the content. Write a client-side script to get it by using the AJAX method and keep it in the cache. This will be a very simple and lightweight solution.
3. Use a modular script loader like RequireJS. This will help give the whole project more modularity.
Here’s how you can improve the performance:

  • If the number of templates is very few, we can use the first method.
  • If many template files exist, we can minify the JS files. But, if you want to keep it more optimal and lightweight by loading only the necessary JS files, use the second or third methods.

Note: We can also reduce the initial page loading time by fetching template files on demand. Usually, there is a limitation on the number of files that can load in parallel. If we load all the files first, more time will be taken and will serve a blank page to the user. To avoid this, we can load only the necessary JS files on demand.

Loading templates by using Require.js
Require.js is a module loader. It loads the necessary libraries, HTML or text files, on demand. Explaining the Require.js functionality is not within the scope of this article. But you can see the basic usage of Require.js in the following example.
Get Require.js from http://requirejs.org/docs/download.html. It also needs the plugin text.js to handle the text or template files. Download it and keep it in the same directory.

Figure 5

Figure 6

modular.html

1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <meta charset=”UTF-8”>
5 <title>Underscore Template - RequireJS sample</title>
6 </head>
7 <body>
8 <h1>Underscore.js Templates and RequireJS</h1>
9 <script src=”jquery-2.1.3.min.js”></script>
10 <script src=”underscore-min.js”></script>
11 <script src=”require.js”></script>
12<script type=”text/javascript”>
13 require.config( {
14 paths:{
15 ‘text’ :’text’,
16 },
17 baseUrl:’’
18 });
19 var stuData = {
20 listTitle: “Students Details”,
21 listItems: [
22 {
23 name: “Kavi”,mark: 89
24 },
25 {
26 name: “Aarthi”,mark: 78
27 },
28 {
29 name: “Bala”,mark: 15
30 },
31 {
32 name: “Shrini”,mark: 45
33 }
34 ]
35 };
36 $( document ).ready(function() {
37 $(“#show”).click(function(){
38 require([‘text!stuTemplate.html’],
39 function(stuTemplate) {
40 _.templateSettings.variable = “x”;
41 var template = _.template(stuTemplate);
42 var tplString = template( stuData );
43 $(“#students”).html(tplString);
44 }
45 );
46 });
47 });
48 </script>
49 <button id=”show” type=”button”>Get Student Details</button>
50 <div id=”students”> </div>
51 </body>
52 </html>

Keep the template file as stuTemplate.html.

<h2> <%- x.listTitle %> </h2>
<ul>
<% _.each( x.listItems, function( listItem ){ %>
<li>
<%- listItem.name %>
<% if ( listItem.mark >30 ){ %>
<em>Pass</em>
<% } else { %>
<em>Fail</em>
<%}%>
</li>
<% }); %>
</ul>

modular.html: An explanation

  • Lines 13-18 define some basic settings for Require.js. They define the plugin name text.js and base URL of the library or text files.
  • When the execution reaches Line 38, it fetches the contents of stuTemplate.html and assigns it into the variable stuTemplate.

Open modular.html in a browser. You will notice that the template file has not loaded on initial page load. Click the button Get Student details. Require.js will make the AJAX request fetch the template file contents.

Advantages of Underscore.js

  • The main advantage of the Underscore template is its simplicity. You don’t need to learn any new syntax. If you know JavaScript coding, you can directly start using this.
  • The precompilation option improves the performance of the code.
  • We can also use other Underscore functions.
  • It is a default template engine for BackboneJS, and it is easy to make it modular by using RequireJS.

If you want to keep your client-side code clean without much effort, try the Underscore.js template.
You can get the working sample codes shown in this article from https://github.com/kbalavignesh/undertemplate_sample

References
[1] http://underscorejs.org/
[2] http://underscorejs.org/#template
[3] http://requirejs.org/
[4] http://www.2ality.com/2012/06/underscore-templates.html

LEAVE A REPLY

Please enter your comment!
Please enter your name here