Precompiling JavaScript Underscore Templates

In this tutorial I will show you how to Precompile JavaScript Underscore Templates with GruntJS. I will be using the GruntJS JST Plugin.

Grunt JS Setup

If you have never used GruntJS before you will need to checkout my Install Grunt JS on a Mac Tutorial or Install Grunt JS on Windows Tutorial.

Now download the example files below and navigate with Terminal or the Command Prompt to the “_build” folder and type in:

npm install

On a Mac you may need to type:

sudo npm install

Basically what “npm install” is doing is looking for a “package.json” file and checks the “devDependencies”. NodeJS will download and install the necessary files for your GruntJS setup. Here is our package file:

{
    "name": "UnderScoreTemplate",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "~0.4.1",
        "grunt-contrib-jst": "~0.5.0"
    }
}

Compiling Underscore Templates into JavaScript

Now that we have the GruntJS packages added to our “_build/node_modules/” folder we can start converting Underscore templates into precompiled JavaScript template Functions.

What the JST GruntJS plugin will do is take the Underscore template (templates/home/UserDetailTemplate.tpl) below and will create a JavaScript function that will return the template as a string.

<%
    var length = userList.length;
    for (var index = 0; index < length; index++) {
        var user = userList[index];
%>
        <tr>
            <td><%= index %></td>
            <td><%= user.firstName %></td>
            <td><%= user.lastName %></td>
            <td><%= user.email %></td>
        </tr>
<% } %>

Here is the function it creates. It uses the path and name of the template as the function name.

this["JST"]["templates/home/UserDetailTemplate.tpl"] = function (obj) {
    obj || (obj = {});
    var __t, __p = '', __e = _.escape, __j = Array.prototype.join;

    function print() {
        __p += __j.call(arguments, '')
    }

    with (obj) {
        var length = userList.length;
        for (var index = 0; index < length; index++) {
            var user = userList[index];
            ;
            __p += '\r\n        <tr>\r\n            <td>' +
                ((__t = ( index )) == null ? '' : __t) +
                '</td>\r\n            <td>' +
                ((__t = ( user.firstName )) == null ? '' : __t) +
                '</td>\r\n            <td>' +
                ((__t = ( user.lastName )) == null ? '' : __t) +
                '</td>\r\n            <td>' +
                ((__t = ( user.email )) == null ? '' : __t) +
                '</td>\r\n        </tr>\r\n';
        };
    }
    return __p
};

To use the precompiled JavaScript template Function in your JavaScript code you would do the following:

var userDetailTemplate = window['JST']['templates/home/UserDetailTemplate.tpl']();
jQuery('body').append(userDetailTemplate);

Since the Underscore template example above expects an Array we will need to pass the data(Array) into the function-call operator as we do below:

var data = [
...
]
var userDetailTemplate = window['JST']['templates/home/UserDetailTemplate.tpl'](data );
jQuery('body').append(userDetailTemplate);

Precompiled JavaScript Template Example

On to my example code which you can download below and see how this all works. I hope code is straight forward.

<script>
    /**
     * Helper Function to get the template function and returns the compiled
     * template string.
     * @param {string} templatePath The path and name of the template you want to use.
     * @param {Object=} data Optional data to be used within the template.
     * @returns {string} Returned compiled template.
     */
    function createTemplate(templatePath, data) {
        var templateString = window['JST'][templatePath](data);
        return templateString;
    }

    /**
     * Initialize Function that gets called when the page is loaded (window.onload).
     * All of our JavaScript code to generate the templates and add it to the DOM.
     */
    function init() {
        // Data for the UserDetailTemplate template.
        var userListData = [
            {
                firstName: 'Mark',
                lastName: 'Otto',
                email: 'mark@example.com'
            },
            {
                firstName: 'Jacob',
                lastName: 'Thornton',
                email: 'jacob@example.com'
            },
            {
                firstName: 'Larry',
                lastName: 'Bird',
                email: 'lary@example.com'
            }
        ];

        // Get templates by path and template name.
        var titleBar = createTemplate('templates/titlebar/TitleBarTemplate.tpl');
        var header = createTemplate('templates/home/HelloTemplate.tpl', { title: 'Robert Is Cool!' });
        var users = createTemplate('templates/home/UserDetailTemplate.tpl', {userList: userListData});

        // Add the template string to the DOM with jQuery.
        var $body = $('body');
        $body.append(titleBar);
        $body.append(header);
        $body.find('tbody').html(users);
    }

    // Calls the init Function on window load.
    window.onload = init;
</script>

Gruntfile

If you dive into the “Gruntfile.js” file you will see:

jst: {
    compile: {
        options: {
            //namespace: "anotherNameThanJST",      //Default: 'JST'
            prettify: false,                        //Default: false|true
            amdWrapper: false,                      //Default: false|true
            templateSettings: {
            },
            processName: function(filename) {
                //Shortens the file path for the template.
                return filename.slice(filename.indexOf("template"), filename.length);
            }
        },
        files: {
            "<%= meta.deployPath %>scripts/templates.js": ["<%= meta.srcPath %>templates/**/*.tpl"]
        }
    }
}

I will point out a couple of things. Look at the files: {…} property. The first part <%= meta.deployPath %>scripts/templates.js is the export path and file name(templates.js) we want the template(s) to be outputted to. The second part [“<%= meta.srcPath %>templates/**/*.tpl”] is where the Underscore templates are located. The templates/**/ means look in the templates directory and all the sub directories. The *.tpl means look for all files that have an extension of .tpl. This will take all those .tpl files and precompile the Underscore templates into JavaScript Functions to a file called templates.js.

Another one I want to point out is the “processName” function. This allows you to modify the file name before the precompiling finishes. By default in my case, all my template Function names would be like below because where my Gruntfile is located:

this["JST"]["../assets/src/templates/home/UserDetailTemplate.tpl"]

I don’t like the look of that and I just wanted to remove everything before the “template” word to get this:

this["JST"]["templates/home/UserDetailTemplate.tpl"]

Now all we have to do is type the command below and it will precompile our Underscore templates.

grunt jst

It maybe best to download the example and start playing around with it.

3 Responses to “Precompiling JavaScript Underscore Templates”

  1. Deepak Gupta Says:
    August 2, 2013 at 11:19 am

    I just loved it. The best and the shortest way to learn grunt in general and also some of its plugins like concat and jst. Many thanks for sharing.

  2. Is this reprinted from somewhere? The text body keep saying “download the example” yet there is no download link provided

  3. This is very helpful, thank you — can’t find a link to the example files anywhere, though…?

Leave a Reply