Reducing JavaScript Code Using jsRender Templates in HTML5 Applications

imageBack in November of 2010 I wrote a post titled Reducing Code using jQuery Templates that demonstrated how jQuery Templates could be used to reduce significant amounts of JavaScript code. Although the topics and code discussed in that post are still valid and relevant in today’s applications, things have a changed some when it comes to the future of jQuery Templates. I’ve had questions come up in the jQuery classes that we offer, at conferences, and online about the future of jQuery Templates so I thought I’d put a post together that provides updated information.

To get the full story you can read Boris Moore’s blog, but in a nutshell the jQuery UI team decided to go a different direction with client-side templates and use an alternate syntax. As a result, a new script has been released called jsRender that will ultimately replace jQuery Templates - at least that’s the current plan. I’ll review a few things mentioned in my initial post here for those that aren’t familiar with templates and then show examples of using the new jsRender functionality in an application.

Nearly every language out there uses templates in some manner to minimize the amount of code that has to be written. By using templates you can define a template structure once and use it to generate code, HTML or other formats. If you’ve created ASP.NET applications then you’re aware of how powerful and productive templates are when it comes to generating HTML output. However, what if you want to use templates on the client-side to generate HTML rather than writing a lot of JavaScript to manipulate the DOM?

Although templates have been available in jQuery for quite awhile through various plug-ins, the jsRender template framework provides a new solution that doesn’t use CSS in strange ways or require a lot of knowledge about a template language. By using it you can define HTML templates in web pages and use them to render HTML output dynamically at runtime. You can download the jsRender script along with samples from https://github.com/BorisMoore/jsrender.

Defining a Template Block

You can use client-side templates by referencing the jsRender script mentioned above and then defining a <script> block in your page with a type of text/x-jquery-tmpl as shown next:

 

<script id="OrderSummaryTemplate" type="text/x-jquery-tmpl">
<!-- Template goes here –>
</script>

 

Once the script tag is defined you can place template code inside of it. Any HTML you add into the template is output automatically once the template is rendered. Of course, adding static HTML doesn’t accomplish much so jsRender provides several tags that can be placed inside of a template to define data that should be output, perform conditional logic, iterate through items, render nested templates, plus more. The key template tags available with jsRender are shown next:

Template Tag Example Description
{{=fieldNameOrExpression}}
{{=DeliveryFee}}
Used to define data values that should be rendered in the template. Evaluates the specified property on the current data item and encodes the value.
{{=fieldNameOrExpression!}}
{{=Comments!}}
Used to define HTML markup strings that should be rendered by the template (notice that a ! character is placed at the end of the string). Evaluates the specified field on the current data item and doesn’t encode the value.
{{#if condition}}
{{#if DeliveryFee > 0}}
{{=DeliveryFee}} added to your order.
{{/if}}
Used for conditional insertion of content.
{{else}}
{{#if MainItems.length===0}}
    <tr>
        <td>No items selected</td>
    </tr>
{{else}}
<tr>
<td>Ordered items!</td>
</tr>   
{{/if}}
Used to add additional conditional logic into jsRender templates.
{{#each}}
{{#each MainItems}}
    <tr>
        <td>
            {{=mi.NumberOrdered}} ordered
at ${{=mi.Price}} per item
        </td>
    </tr>
{{/each}}
Used to iterate over a data array and render the content for each data item.
{{#each tmpl=”#NestedTemplateID”}}
<script id="movieTemplate" type="text/x-jquery-tmpl"> 
    {{#each Movies tmpl="#titleTemplate"}}
</script>

<script id="titleTemplate" type="text/x-jquery-tmpl"> 
    <tr class="title">
<td>{{=Name}}</td>
</tr> </script>
Used for composition of templates. Renders one or more nested template items within the rendered output of the parent template.

 

Rendering a Template

Once a template is defined using a <script> block you can use jsRender’s render() function to convert data into HTML based upon the template. This is done by identifying the target element that will host the content, calling its html() function (when using jQuery). and then passing the HTML that’s generated by calling the render() function. The render() function accepts the data that the template will use to generate HTML content. An example of putting all of this together is shown next. You’ll see that the OrderSummaryOutput element is located in the DOM using a jQuery selector and that its html() function is passed the output generated by a jsRender template.

 

$("#OrderSummaryOutput").html($("#OrderSummaryTemplate").render(data));

 

The data can be created locally from an object literal or retrieved from a remote service call as JSON as shown nextt:

 

$.ajax({
    dataType: 'jsonp',
    url: moviesServiceUrl,
    jsonp: '$callback',
    success: showMovies
});

// Within the callback, use .tmpl() to render the data.
function showMovies(data)
{
    // Render the template with the "movies" data and insert
    // the rendered HTML under the 'movieList' element
    $("#movieList").html($("#moviesTemplate").render(data));
}

 

jsRender in Action

A sample application that I created to demonstrate jsRender in action can be downloaded here (it’s part of the sample code available with our jQuery Web Programming training course). The sample app is an ASP.NET MVC 3 project named “Order Up” that leverages jQuery heavily and uses jsRender to display order details. An example of the output that’s rendered is shown next:

image


The template used to generated the Totals, Delivery Information, Items Ordered and Accessories Ordered sections is shown next:

 

<script id="OrderSummaryTemplate" type="text/x-jquery-tmpl">
    <table style="width:100%;">
        <tbody>             
            <tr>
                <td class="OrderHeader">Totals:</td>                    
            </tr>                    
            <tr>
                <td style="font-size:12pt;">
                    <table style="width:400px;">
                        <tr>
                            <td style="width:50%;">Sub Total:</td>
                            <td>$<span id="FinalSubTotal">{{=FinalSubTotal}}</span></td>
                        </tr>
                        <tr>
                            <td style="width:50%;">Sales Tax:</td>
                            <td>$<span id="FinalSalesTax">{{=FinalSalesTax }}</span></td>
                        </tr>
                        {{#if DeliveryFee > 0}}
                            <tr>
                                <td style="width:50%;">Delivery Fee:</td>
                                <td>$<span id="FinalDeliveryFee">{{=DeliveryFee }}</span></td>
                            </tr>
                        {{/if}}
                        <tr>
                            <td style="width:50%;">Admin Fee:</td>
                            <td>$<span id="FinalAdminFee">{{=AdminFee }}</span></td>
                        </tr>
                        <tr style="border-top:1px solid black;">
                            <td style="width:50%;font-weight:bold;">Total:</td>
                            <td>$<span id="FinalTotal">{{=FinalTotal }}</span></td>
                        </tr>   
                        <tr>
                            <td colspan="2">&nbsp;</td>
                        </tr>
                        <tr>
                            <td colspan="2">Will be charged to your credit card ending with {{=CreditCard }}</td>
                        </tr>                     
                    </table>                        
                </td>
            </tr> 
            <tr>
                <td>&nbsp;</td>
            </tr> 
            <tr>
                <td class="OrderHeader">Delivery Information</td>                    
            </tr>
            <tr>
                <td>
                    <table style="width:500px;">
                        <tr>
                            <td style="width:25%;">Deliver To:</td>
                            <td>{{=DeliveryName}}</td>
                        </tr>
                        <tr>
                            <td style="width:25%;">Address:</td>
                            <td>{{=DeliveryAddress}}</td>
                        </tr>
                        <tr>
                            <td style="width:25%;">Date and Time:</td>
                            <td>{{=DeliveryDate}} from {{=DeliveryTime}}</td>
                        </tr>                        
                    </table>                         
                    </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
            </tr>                    
            <tr>
                <td class="OrderHeader">Items Ordered</td>
            </tr> 
            {{#if MainItems.length === 0}}
                <tr>
                    <td>No items selected</td>
                </tr>
            {{else}}
                {{#each MainItems tmpl="#ItemsTemplate"}}
            {{/if}}
            <tr>
                <td>&nbsp;</td>
            </tr>   
            <tr>
                <td class="OrderHeader">Accessories Ordered</td>
            </tr> 
            {{#if AccessoryItems.length === 0}}
                <tr>
                    <td>No accessories selected</td>
                </tr>
            {{else}}
                {{#each AccessoryItems tmpl="#ItemsTemplate"}}
            {{/if}}
            <tr>
                <td>&nbsp;</td>
            </tr>                          
        </tbody>
    </table>    
</script>

<script id="ItemsTemplate" type="text/x-jquery-tmpl">
    <tr>
        <td>
            {{=Name}} - {{=NumberOrdered}} ordered at $ {{=Price}} per item
        </td>
    </tr>
</script>
 

The template is rendered to a div with an ID of OrderSummaryOutput using the following code.  The code first creates a JSON object by retrieving data from controls in a checkout wizard and then calls the the render() function provided by jsRender.

 

function LoadApprovalDiv() {
    var subTotal = parseFloat($('#SubTotal').text());
    $('#ClientSubTotal').val(subTotal.toFixed(2));
    var salesTaxRate = parseFloat($('#SalesTaxRate').val()) / 100;
    var salesTaxAmount = (subTotal * salesTaxRate) * .9;
    var deliveryFee = parseFloat($('#DeliveryFee').val());
    var adminFee = ((subTotal + salesTaxAmount + deliveryFee) * .05);
    var total = (Round(subTotal) + Round(salesTaxAmount) + Round(deliveryFee) + 
                 Round(adminFee));
    $('#ClientTotal').val(total);
    var deliveryAddress = $('#Delivery_Street').val();
    //See if they entered a suite
    if ($('#Delivery_Suite').val() != '') deliveryAddress += ', Suite ' + $('#Delivery_Suite').val();
    deliveryAddress += ' ' + $('#Delivery_City').val() + ' ' + $('#Delivery_StateID option:selected').text() + ' ' + 
                       $('#Delivery_Zip').val();
    var creditCard = $('#Payment_CreditCardNumber').val();
    var abbrCreditCard = '*' + creditCard.substring(creditCard.length - 5);

    var data= {
                   'FinalSubTotal'  : subTotal.toFixed(2),
                   'FinalSalesTax'  : salesTaxAmount.toFixed(2),
                   'FinalTotal'     : total.toFixed(2),
                   'DeliveryFee'    : deliveryFee.toFixed(2),
                   'AdminFee'       : adminFee.toFixed(2),
                   'DeliveryName'   : $('#Delivery_Name').val(),
                   'DeliveryAddress': deliveryAddress,
                   'CreditCard'     : abbrCreditCard,
                   'DeliveryDate'   : $('#Delivery_DeliveryDate').val(),
                   'DeliveryTime'   : $('#Delivery_DeliveryTime option:selected').text(),
                   'MainItems'      : GenerateData('Main'),
                   'AccessoryItems' : GenerateData('Accessory')
               };

    //jQuery template example
    $("#OrderSummaryOutput").html($("#OrderSummaryTemplate").render(data));
}


If you’re working with dynamic web applications that leverage jQuery and AJAX you’ll find that jsRender can significantly increase your productivity and eliminate a lot of code that you’d normally have to write. Although jsRender is still new (at the time this post was written anyway), it’s definitely worth looking into more. In future posts I’ll discuss a companion to jsRender called jsView that provides client-side data binding capabilities.

 

 

Logo_702D5F60[1]

If you or your company is interested in training, consulting or mentoring on jQuery, HTML5,  or .NET technologies please visit http://www.thewahlingroup.com for more information. We’ve provided training, consulting and mentoring services to some of the largest companies in the world and would enjoy sharing our knowledge and real-world lessons learned with you.

comments powered by Disqus

9 Comments

  • Nice post Dan! JsRender is very promising and while most of us are used to jQuery Templates, the transition is pretty smooth and the benefits (perf) are nice due to the string based rendering.

    Looking forward to its beta release!

  • Thanks John - looking forward to your article series on the topic.

  • thanks this information

  • Good article! I wonder when new Project Silk with knockout.js and jsRender is going to be released. It would be a great reference project for these things!

  • What kind of ide tooling is available for jsrender? I know for js templates I might as well be coding in notepad. Jstemplates are very hard to debug... Any improvement here with jsrender?

  • Caveat, here is a message from the site: Warning: JsRender is not yet Beta, and there may be frequent changes to APIs and features in the coming period.

  • Just a question: why do you state that you create a JSON object and call your variable json when what you are in fact doing is creating a javascript object, not something json-specific?

  • Cros:

    Thanks for commenting. I generally refer to simple name/value pairs in an object as JSON vs. name/value pairs with embedded functions as object literals even though that's a loose use of the term "JSON" I know. Ultimately they're all object literals (JSON is simply a subset as I'm sure you're aware). Officially JSON is just the data exchange format and requires quoted property names, etc. but I like to distinguish between name/value pair (simple objects) vs. objects that have actual functionality (object literals). Just personal preference.... tomato vs. tamato type of thing. :-)

    Given that the two are distinctly different I probably should be more careful to "officially" distinguish between the two so I appreciate the comment and will update things to be a bit more clear there especially for people who may not know the difference between the two.

    Dan

  • Having used knockout 1.2 and seeing what's in 2.0, at first glance it seems hard to grok why I would use jquery-tmpl instead (outside of not wanting to wrap my data into a viewmodel for some reason).

    What scenarios does jquery-tmpl still 'win' as the best choice for a given scenario?

Comments have been disabled for this content.