Friday, September 6, 2013

Shredder Tips : Mustache.js for Cleaner JavaScript


This post is the first in a series that I will be writing to share some of the techniques and tools we use at Byte Shredders.  The intended audience of this series is software and web developers of all skill levels.  I am always learning about new programming styles, tools, and shortcuts.  I hope to share with the programming community some of the knowledge that I've gathered from reading other blogs and working with clients (not to mention looking at Stack Overflow!).

Beautiful, easy to understand code - something every software craftsman strives for.  The last few months, I've been focused on improving the readability of my JavaScript.  Generating and displaying web page content after the page has first loaded can become quite messy.  To solve this problem, I started looking into client side template systems.  My favorite solution is mustache.js - named after the look of the curly brace syntax.  I've used it in a few projects already, and I've been satisfied with the results.


To illustrate how mustache.js can help, I prepared two working examples that render some JSON data in different ways. The first section of code below shows one method I have used in the past.  It uses jQuery to iterate over each item in the OrderLines array, and dynamically create HTML elements.  After each row is created, it is added to the end of the existing table.

var orderData = {
  OrderNumber: "1",
  CustomerName: "Cat Traders Inc.",
  OrderLines: [
    { LineNumber: 1, Item: "American Shorthair", UnitPrice: 149.99, URL: "#"},
    { LineNumber: 2, Item: "Persian", UnitPrice: 2499.99, URL: "#"},
    { LineNumber: 3, Item: "Scottish Fold", UnitPrice: 899.99, URL: "#"},
    { LineNumber: 4, Item: "Bombay", UnitPrice: 399.99, URL: "#"}
  ]
};

$.each(orderData.OrderLines, function(i, orderline) {
    var itemRow = $("<tr/>");
    $("<td/>").text(orderline.LineNumber).appendTo(itemRow);
       
    itemlink = $("<a/>", { href: orderline.URL, text: orderline.Item});
    $("<td/>").append(itemlink).appendTo(itemRow)

    $("<td/>").text(orderline.UnitPrice).appendTo(itemRow); 
    $("#orderTable").append(itemRow)
});

<table id="orderTable">
        <tr>
            <th>Line</th>
            <th>Item</th>
            <th>Price</th>
        </tr>
</table>
Live example - http://jsfiddle.net/kgUkz/

As you add more tags and attributes using this method, the JS becomes increasingly cryptic.  Now take a look at the mustache style below.  All of the work is done by three lines of JavaScript.  The nice thing about this method is that you can look at the template and immediately have an idea of the output's HTML structure.

The most beneficial reason for using mustache is the ease of changing the templates. When a client required more data on their site, I didn't have to look back at my code and figure out how the the section was being generated.  After modifying the web service to return another field from the database, displaying it was as simple as adding an HTML table column. 

var orderData = {
  OrderNumber: "1",
  CustomerName: "Cat Traders Inc.",
  OrderLines: [
    { LineNumber: 1, Item: "American Shorthair", UnitPrice: 149.99, URL: "#"},
    { LineNumber: 2, Item: "Persian", UnitPrice: 2499.99, URL: "#"},
    { LineNumber: 3, Item: "Scottish Fold", UnitPrice: 899.99, URL: "#"},
    { LineNumber: 4, Item: "Bombay", UnitPrice: 399.99, URL: "#"}
  ]
};

//Use jQuery to load the template 
var orderTemplate = $('#ordertable').html();

//Use mustache to populate the template with data
var html = Mustache.to_html(orderTemplate, orderData);

//Use jQuery to replace the contents of #orders with our html output
$('#orders').html(html);
<script id="ordertable" type="text/template">
    <h3>#{{OrderNumber}} - {{CustomerName}}</h3>
    <table>
        <tr>
            <th>Line</th>
            <th>Item</th>
            <th>Price</th>
        </tr>
    {{#OrderLines}}  
        <tr>
            <td>{{LineNumber}}</td>
            <td><a href="{{URL}}">{{Item}}</a></td>
            <td>${{UnitPrice}}</td>
        </tr>
    {{/OrderLines}}
    </table>
</script>

<h1>Open Orders:</h1>
<div id="orders"></div>
Live example - http://jsfiddle.net/45rHt/

Now that you've seen it in action, I'll explain the key features used here.  Any JavaScript variables in the object you pass to mustache are accessible via the double curly braces notation - {{VariableName}}.  The main benefit of mustache is when you're looping through an array, and don't have to write JS code for it.  You can start a section that is repeated for each item in an array with {{#ArrayName}} and end it with {{/ArrayName}}.  Inside the array block, you can directly access each array item's variables by its name in double curly braces. 

What happens if the object you pass in is an array?  Mustache recognizes a period as a reference to the current item.  Using {{#.}} and {{/.}} will let you iterate through each item in the array.  Likewise, if the items in the array are primitive data types instead of named values, you can access them with {{.}}.

var html = Mustache.to_html("<ul>{{#.}}<li>{{.}}</li>{{/.}}</ul>", [0,1,2,3]);
$('#example').html(html);
Live example - http://jsfiddle.net/FHCzF/

Another thing to note is that the template doesn't have to come from a script tag, as you can see above.  The text/template script is just my personal preference to separate it from other content in the file.  Mustache's function just expects a string, so some other ways you can load the template are from an inline string, a web service call, or an external file.  There are plenty of other features in mustache, so check out the project's GitHub if you think it can help you out.


Feel free to leave a comment if you have a different way of doing things that you want to share, or if you think my cat prices are wildly inaccurate.  The link to get mustache is below.  If you found this interesting, take a look at the Handlebars and Underscore.

Remember to follow us on Facebook to stay connected with our latest tips!

No comments: