Using Underscore.js Templates To Render HTML Partials

Posted August 17, 2012 at 10:38 AM by Ben Nadel

Tags: Javascript / DHTML

I've never used Underscore.js before. But, we're going to start using some Backbone.js at InVision; and, since Underscore.js is a hard dependency of Backbone.js, we decided to use Underscore's template() method as our client-side HTML partial rendering engine.

All client-side template engines attempt to answer the same question: How do I merge this data into this block of HTML? Solutions can range from the complex - creating a DSL (Domain Specific Language) like the jQuery Template Markup Language (jtml) - to the simple - embedding raw JavaScript directives within your template. The Underscore.js template() method takes the latter approach, providing simple hooks for executing JavaScript in and around parts of your template's HTML.

This low-level approach can lead to some strange looking template markup; but, it definitely provides for the most power and flexibility. As you'll see in the demo below, my template can make full use of all JavaScript functionality, including the underscore library itself.

By default, the template() method uses JavaScript's "with(..)" directive to help locate unscoped variable references. This is considered an anti-pattern; and, it is something that is deprecated in future versions of JavaScript. As such, Underscore.js also allows you to define a scope variable in which you will make your top-level variable references. Since we use the "rc" - Request Collection / Request Context - variable on the server side, I decided to update the root Underscore.js template settings to use "rc" on the client-side:

  • _.templateSettings.variable = "rc";

Now, all top-level variable references in our Underscore.js templates have to be referenced off of, "rc."

You can also override the ERB (Embedded Ruby) style markup, if you want to use braces (ala Mustache or Handlebars) instead of angle-brackets. Both approaches kind of look equally funny to me, however, so I just stuck with the defaults.

That said, let's take a look at my fist use of the Underscore.js template engine:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  • <title>Looking At Underscore.js Templates</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Looking At Underscore.js Templates
  • </h1>
  •  
  •  
  • <!-- BEGIN: Underscore Template Definition. -->
  • <script type="text/template" class="template">
  •  
  • <h2>
  • <%- rc.listTitle %>
  • </h2>
  •  
  • <ul>
  • <% _.each( rc.listItems, function( listItem ){ %>
  •  
  • <li>
  •  
  • <%- listItem.name %>
  •  
  • <% if ( listItem.hasOlympicGold ){ %>
  • <em>*</em>
  • <% } %>
  •  
  • </li>
  •  
  • <% }); %>
  • </ul>
  •  
  •  
  • <% var showFootnote = _.any(
  • _.pluck( rc.listItems, "hasOlympicGold" )
  • ); %>
  •  
  •  
  • <% if ( showFootnote ){ %>
  •  
  • <p style="font-size: 12px ;">
  •  
  • <em>* Olympic gold medalist</em>
  •  
  • </p>
  •  
  • <% } %>
  •  
  • </script>
  • <!-- END: Underscore Template Definition. -->
  •  
  •  
  • <!-- Include and run scripts. -->
  • <script type="text/javascript" src="../jquery-1.8.0.js"></script>
  • <script type="text/javascript" src="../underscore.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // When rending an underscore template, we want top-level
  • // variables to be referenced as part of an object. For
  • // technical reasons (scope-chain search), this speeds up
  • // rendering; however, more importantly, this also allows our
  • // templates to look / feel more like our server-side
  • // templates that use the rc (Request Context / Colletion) in
  • // order to render their markup.
  • _.templateSettings.variable = "rc";
  •  
  • // Grab the HTML out of our template tag and pre-compile it.
  • var template = _.template(
  • $( "script.template" ).html()
  • );
  •  
  • // Define our render data (to be put into the "rc" variable).
  • var templateData = {
  • listTitle: "Olympic Volleyball Players",
  • listItems: [
  • {
  • name: "Misty May-Treanor",
  • hasOlympicGold: true
  • },
  • {
  • name: "Kerri Walsh Jennings",
  • hasOlympicGold: true
  • },
  • {
  • name: "Jennifer Kessy",
  • hasOlympicGold: false
  • },
  • {
  • name: "April Ross",
  • hasOlympicGold: false
  • }
  • ]
  • };
  •  
  • // Render the underscore template and inject it after the H1
  • // in our current DOM.
  • $( "h1" ).after(
  • template( templateData )
  • );
  •  
  •  
  • </script>
  • </body>
  • </html>

As you can see, the template markup is a bit funny to read. But, if you look at it slowly, you can see that it's nothing more than raw JavaScript markup mixed in with HTML markup (which all gets compiled down to a render Function and a print buffer behind the scenes). It is important to understand that the rendering executes as a function so that you'll remember to "var" your local variables. In this case, you can see that I created an intermediary, template-local variable, "showFootnote", in order to determine if my view needs to render a particular portion of the template.

Right now, my template is embedded within a non-JavaScript Script tag located within my main page. This is easy to do for the demo; but it is not required. Your template could easily (and probably should be) placed in an external HTML file that is subsequently loaded with something like RequireJS.

Anyway, that's my first look at Underscore.js and it's template() method. Pretty cool stuff. I can see why people rave about Underscore. It has a boat-load of useful functions.




Reader Comments

Aug 17, 2012 at 11:03 AM // reply »
5 Comments

Looks good, I've been using the Micro-templating function by John Resig(http://ejohn.org/blog/javascript-micro-templating/) for a while now which is lightweight. Do you know how the underscore.js template function does it performance wise?


Aug 17, 2012 at 11:37 AM // reply »
11,246 Comments

@Ralph,

I believe that all of the template engines work the same way (more or less) as the micro-templating by Resig. From the ones I've looked at, they use an array behind the scenes, build it up, and then join() it and return the string. I assume they all have roughly the same performance.

Underscore.js allows you to use a scope variable for references; which, is faster than using with(). So, that's a performance boost.


Aug 17, 2012 at 12:29 PM // reply »
9 Comments

Underscore's boatload of useful functions is exactly why I wrote Underscore.cfc: http://russplaysguitar.github.com/UnderscoreCF/

I didn't implement templating though, since there is already a CF version of Mustache: https://github.com/pmcelhaney/Mustache.cfc


Aug 17, 2012 at 1:30 PM // reply »
5 Comments

One thing to be aware of is that underscore's template, unlike other (such as Handlebars), does not do any HTML escaping for you. You can do it manually using _.escape. This is obviously important when it comes to preventing cross-sites-scripting vulnerabilities.


Aug 18, 2012 at 10:15 AM // reply »
11,246 Comments

@Russ,

Very cool. I'm working on moving to ColdFusion 10; but, I'm waiting for a license to come through (supposedly processing). I know you can probably use this stuff pre-CF10; but, using the function expressions probably makes life much easier!

@Stephen,

I think they may have updated it to include escaping. It looks like you have to use a slightly different variation in the ERB notation to allow for escaping:

  • <%= ... interpolation ... %>
  • <%- ... escaped ... %>
  • <% ... execute ... %>

Notice the use of "-" rather than "=". The docs say that this will HTML escape it.


Aug 18, 2012 at 10:34 AM // reply »
5 Comments

@Ben,

Wow, you're right, and it's been that way for a while. I guess the stuff I'd read previously about it is just very out of date. Sorry for the misinformation.


Aug 22, 2012 at 1:24 AM // reply »
1 Comments

Hey Ben! If ya dig Underscore/Backbone you may dig Lo-Dash (http://lodash.com), a drop-in replacement for Underscore that's customizable. If ya only want the `_.template` method you can do `lodash include=template` to create a build with just `_.template`, or if you want to use Lo-Dash with Backbone and a different template lib you can do `lodash backbone` to create a build of Lo-Dash with only the methods used by Backbone. Lo-Dash also has optimizations to automatically avoid with-statements for templates that don't use "evaluate" delimiters and sourceURL's baked into each template to help debug them in development builds.


Aug 23, 2012 at 2:24 PM // reply »
11,246 Comments

@Stephen,

No problem at all! Since this is my first time looking into it, I had completely fresh eyes. I know how it is - with ColdFusion, I've been through versions 4.5 -> 10. Sometimes, I'll get through 2,3,4 versions without even realizing that a method was added :D

@John-David,

I've heard good / funny things about lo-dash. I forget what Pod cast it was (JavaScript Jabber? Ruby Rouges?) where they were saying that you and the guy behind Underscore have some sort of ongoing rivalry where you keep fixing his bugs - that's too funny :D I'll definitely check it out.


Sep 10, 2012 at 10:46 AM // reply »
3 Comments

Although I like Handlebars.js, we used Mustache.cfc with Mustache.js successfully on a large project. It was wonderful having both ColdFusion and JavaScript use the same templates...


Sep 22, 2012 at 5:42 PM // reply »
11,246 Comments

@James,

It's funny you mention that; a couple of times, I have starting an effort to create a JavaScript parser for a small set of ColdFusion tags (so that you could essentially use the same template in both places). Then I give up :) But, it would be something to keep trying again.


Sep 23, 2012 at 4:32 PM // reply »
3 Comments

Yeah, take a look at Mustache. It might be a good starting point.

Also, I'm not sure it's related but it reminded me of ColdFusion/JavaScript, take a look at this lib:
http://cfjs.riaforge.org/


Nov 29, 2012 at 6:15 PM // reply »
1 Comments

This is good stuff. However, I have yet to find a good example of how to nest templates (templates that call other templates). Any ideas on that?

Thanks for the article!


Dec 6, 2012 at 11:14 AM // reply »
1 Comments

Daniel Israel,

If you are looking for templates that call other templates you could see Google Closure (https://developers.google.com/closure/). I have working with it and liked to much. However, the bad point is that you need to build it... There is a way to config it on Visual Studio...


Dec 31, 2012 at 4:13 PM // reply »
1 Comments

thanks for the useful article - it help me understand templating with backbone - not sure what the rage is about regarding data binding and javascript this looks like classic asp or echoing out some php


May 3, 2013 at 7:02 AM // reply »
1 Comments

This article is really helpful Ben Nadel.
Thanks for this post :)



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
May 23, 2013 at 5:19 AM
Ask Ben: Print Part Of A Web Page With jQuery
How to print also the background color of table cells and table lines ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools