Productively Distracted
Posted Wednesday February 22, 2012 around 9:05 PM

Did you know that the rails 3.1+ asset pipeline can pre-compile your javascript templates for you? It's actually very easy to do and can give your clients a nice little speed bump.

Asset Pipeline File Types

A quick word on how sprockets processes assets. The asset pipeline is actually just a tight integration between rails and the excellent sprockets gem. Sprockets runs files through a pre-processor which allows it to work with all different kinds of formats. It does this by walking through the files extensions in reverse order. So to use Sass for a CSS file you can simply name it application.sass and it will be processed by sass and output as application.css. If sprockets encounters a file extension it doesn't know it will stop processing there. The final output extension is determined by either the last extension used or the mime type for the last used processor. So a file named application.js.coffee would be output to application.js and a file named application.coffee would be output to application.js as well.

The JstProcessor

Adding the extension .jst to your file will instruct sprockets to create a javascript file that wraps the contents of your file. For example, if I create a file named helloworld.jst with a very simple function in it like this:

function(){ return "<h1>Hello World</h1>"; }
Download

The JstProcessor will output:

(function() {
  this.JST || (this.JST = {});
  this.JST["hellworld"] = function(){ return "<h1>Hello World</h1>"; };
}).call(this);
Download

This means you can put any valid javascript in your file and JstProcessor will make it available on the JST object. The above could even be simplified to:

"<h1>Hello World</h1>";
Download

And the output would be:

(function() {
  this.JST || (this.JST = {});
  this.JST["hellworld"] = "<h1>Hello World</h1>";
}).call(this);
Download

Template Processors

So with the JstProcessor as the final processor, we are ready to add a templating library to the processing chain. The entry level javascript template engine is the EJS processor. It is a ruby port of underscore templates. Think of it like ERB for javascript. To use it just add gem 'ejs' to your Gemfile, and add the extension .ejs to your template. A quick example helloworld.jst.ejs:

<h1>Hello World</h1>
<p>The total of 1 + 1 = <%= 1 + 1 %></p>
Download

Outputs to helloworld.js:

(function() {
  this.JST || (this.JST = {});
  this.JST["helloworld"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<h1>Hello World</h1>\n<p>The total of 1 + 1 = ', 1 + 1 ,'</p>\n');}return __p.join('');};
}).call(this);
Download

While EJS is great and all, there are lots of other javascript languages out there. Personally I love jade, but here is a list of the template engines I was able to find that are compatible with sprockets:

Using Templates

Before you can access the template you will need to add it to your view. This is done the standard rails way using javascript_include_tag:

!!!
%html
  %head
    %title Hello World
    = javascript_include_tag 'helloworld'
    = csrf_meta_tags
  %body
Download

Using the templates is completely straight forward. If you look at the last example you can see that the template function is saved on the JST global object. So a typical usage would look something like this:

$(function(){
    $("body").html(JST.helloworld({}));
});
Download

When you have large apps with many templates you can name-space your templates by placing them in sub-directories. If instead of placing helloworld.jst.ejs in the root of my javascripts folder, I put it in /javascripts/posts/helloworld.jst.ejs, I will get a file compiled to this:

(function() {
  this.JST || (this.JST = {});
  this.JST["posts/helloworld"] = function(obj){var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('<h1>Hello World</h1>\n<p>The total of 1 + 1 = ', 1 + 1 ,'</p>\n');}return __p.join('');};
}).call(this);
Download

So now when I want to use this template I just access it through "posts/helloworld" on the JST global object like this:

$(function(){
    $("body").html(JST['posts/helloworld']({}));
});
Download

I like to create a templates folder in my assets folder to keep all of my javascript templates in. That way I can just do a // require_tree ../templates in my application.js to stitch them all together at runtime.

Edit 08/08/2012: Added link to hogan_assets.

blog comments powered by Disqus