Book Image

Backbone.js Patterns and Best Practices

By : Swarnendu De
Book Image

Backbone.js Patterns and Best Practices

By: Swarnendu De

Overview of this book

Table of Contents (19 chapters)
Backbone.js Patterns and Best Practices
Credits
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Precompiling Templates on the Server Side
Index

Appendix B. Precompiling Templates on the Server Side

In Chapter 2, Working with Views, we learned the advantages of using precompiled templates in your application. In addition, we saw a number of options to store your templates as inline in the index.html file or as separate template files. We also saw how we can use a template manager to precompile and cache templates to avoid compilation overhead every time. However, this precompilation process will run anyway when you start your application, which will surely take a certain period of time. Wait! Aren't these templates static resources? Then the compiled versions of the templates without data will also be static resources. Right? Then why not keep a separate file with all of the precompiled templates ready and use it as soon as your application starts? If you get a file with all of your templates already precompiled and minified, it will certainly boost your application's performance. This is what we will try here—we will develop a script to precompile the templates on the server side, which will traverse all of the template files and create a single template manager file. We use Node.js here, but you can use any server-side technology to get the same result. The complete working code is given in our code samples.

To precompile, we need a template engine with precompilation support. We will use Underscore.js here, but you are free to use your desired template engine to achieve the result. The following Node.js example shows you how to achieve this functionality:

// load the file system node module
var fs = require('fs'),
  // load the underscore.js
  _ = require('../../../lib/underscore.js');

var templateDir = './templates/',
  template,
  tplName,

  // create a string which when evaluated will create the 
  // template object with cachedTemplates
  compiledTemplateStr = 'var Templates = {cachedTemplates : {}}; \n\n';

// Iterate through all the templates in templates directory
fs.readdirSync(templateDir).forEach(function (tplFile) {

  // Read the template and store the string in a variable
  template = fs.readFileSync(templateDir + tplFile, 'utf8');

  // Get the template name
  tplName = tplFile.substr(0, tplFile.lastIndexOf('.'));

  // Add template function's source to cachedTemplate
  compiledTemplateStr += 'Templates.cachedTemplates["' + tplName + '"] = ';
  compiledTemplateStr += _.template(template).source + '\n\n';
});

// Write all the compiled code in another file
fs.writeFile('compiled.js', compiledTemplateStr, 'utf8');

The preceding code is pretty self-explanatory. We created a complete JavaScript snippet as a string that will be returned to the frontend. Here are the steps to do so:

  1. First, we browse through each template file in the templates directory and retrieve their contents.

  2. We already have an object Templates.cachedTemplates defined and we need to store each template file's contents in this object with the template filename as a property and the template string as its value.

  3. Underscore's _.template() method, in general, returns a function. It also provides a property called source that gives the textual representation of that particular function. The following line will give you the function source code:

    _.template(template).source
  4. We place all of the function strings inside Templates.cachedTemplates one by one, and once the loop is over, we write the entire contents to another JavaScript file.

Now assume that the client side is asking for the templates.js file that contains the complete template content of the project. On the server side, we can write the following code that will send the compiled.js file content to the browser:

// While templates.js file is loaded, it will 
// send the compiled.js file's content
app.get('/templates.js', function (req, res) {
  res
    .type('application/javascript')
    .send(fs.readFileSync('compiled.js', 'utf8'));
});

So, a request to the template.js file on the client side will display content similar to the following code:

var Templates = {
  cachedTemplates: {}
};

Templates.cachedTemplates["userLogin"] = function (obj) {
  var __t, __p = '',
    __j = Array.prototype.join,
    print = function () {
      __p += __j.call(arguments, '');
    };
  with(obj || {}) {
    __p += '<ul>\r\n    <li>Username:\r\n        <input type="text" value="' +
      ((__t = (username)) == null ? '' : __t) +
      '" />\r\n    </li>\r\n    <li>Password:\r\n        <input type="password" value="' +
      ((__t = (password)) == null ? '' : __t) +
      '" />\r\n    </li>\r\n</ul>\r\n';
  }
  return __p;
}

The final output is the TemplateManager object with the template's filename as its property and the compiled version of the template as the value of that property. This way, all of your template files will get added to the TemplateManager object. However, for this piece of code, you need to make sure that each template's filename is different. Otherwise, the template of the files with the same name will get overwritten by another.

You do not need to understand this compiled template function definition, as this will be used internally by the library. Be assured that once you call this function with the data object, you will get the proper HTML output:

var user = new Backbone.Model({
  username: 'hello',
  password: 'world'
});

// Get the html
var html = Templates.cachedTemplates.userLogin(user.toJSON());

This solution for precompiling JavaScript templates is very effective and you can use the same concept freely in your projects. We have used this concept in multiple projects successfully.