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>"; }
The JstProcessor will output:
(function() {
this.JST || (this.JST = {});
this.JST["hellworld"] = function(){ return "<h1>Hello World</h1>"; };
}).call(this);
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>";
And the output would be:
(function() {
this.JST || (this.JST = {});
this.JST["hellworld"] = "<h1>Hello World</h1>";
}).call(this);
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>
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);
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:
- ejs - Underscore style templates.
- jade - A cleaner HAML ported to javascript.
- eco - Coffescript templates.
- handlebars_assets - Handlebars templates.
- ruby-haml-js - A direct HAML port to javascript.
- hogan_assets - Uses hogan.js to precompile mustache templates.
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
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({}));
});
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);
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']({}));
});
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.