Reducing Code by Using jQuery Templates

imageNearly every language out there uses templates in some shape or form to minimize the amount of code that has to be written in an application. 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 latest template framework worked on jointly by Microsoft and the jQuery team provides a great 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. I’ll cover the basics of using the jQuery Templates in this post but you can get additional information from http://api.jquery.com/category/plugins/templates.

You can download the jQuery Templates script here along with a sample application or reference one of the following Microsoft CDN scripts:

Defining a Template Block

Templates are defined by referencing the template 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 that you add is output automatically once the template is rendered. Of course, adding static HTML doesn’t accomplish much so jQuery Templates provides a template tag language that contains 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 different template tags available with jQuery Templates are are shown next (descriptions and some of the examples are from the jQuery Templates documentation):

Template Tag Example Description
${fieldNameOrExpression} ${ DeliveryFee } Used for insertion of data values in the rendered template. Evaluates the specified field (property) on the current data item, or the specified JavaScript function or expression.
{{html fieldNameOrExpression}} {{html Comments}} Used for insertion of HTML markup strings in the rendered template. Evaluates the specified field on the current data item, or the specified JavaScript function or expression.
{{if}}
{{if DeliveryFee > 0}}
${ DeliveryFee } added to your order.
{{/if}}
Used for conditional insertion of content. Renders the content between the opening and closing template tags only if the specified data item field, JavaScript function or expression does not evaluate to false (or to zero, null or undefined).
{{else}}
{{if MainItems.length==0}}
    <tr>
        <td>No items selected</td>
    </tr>
{{else}}
<tr>
<td>Ordered items!</td>
</tr>   
{{/if}}
Used in association with the {{if}}...{{/if}} tag to provide alternative content based on the values of one or more expressions. The {{else}} tag can be used without a parameter, as in: {{if a}}...{{else}}...{{/if}}, or with a parameter, as in: {{if a}}...{{else b}}...{{/if}}.
{{each}}
{{each(i,mi) 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 between the opening and closing template tags once for each data item.
{{tmpl}}
<script id="movieTemplate" type="text/x-jquery-tmpl"> 
    {{tmpl "#titleTemplate"}}
    <tr>
<td>${ Director }</td>
</tr> </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.
{{wrap}}
<script id="myTmpl" type="text/x-jquery-tmpl">
    The following wraps some HTML content:
    {{wrap "#tableWrapper"}}
        <div>
            First <b>content</b>
        </div>
        <div>
            And <em>more</em> <b>content</b>...
        </div>
    {{/wrap}}
</script>

<script id="tableWrapper" type="text/x-jquery-tmpl">
    <table><tbody>
        <tr>
            {{each $item.html("div")}}
                <td>
                    {{html $value}}
                </td>
            {{/each}}
        </tr>
    </tbody></table>
</script>
Used for composition of templates which incorporate wrapped HTML content. Rendered template items can combine wrapped HTML content with template markup.

 

Rendering a Template

Once a template is defined using a <script> block you can use the jQuery Templates tmpl() function to render the template to a container. This is done by identifying the target template, calling tmpl and passing a JSON object into it and then defining where the rendered output should go:

$('#OrderSummaryTemplate').tmpl(json).appendTo('#OrderSummaryOutput');

The JSON data can be created locally or retrieved from a remote service call as shown next:

 

$.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
    $('#movieTemplate').tmpl(data).appendTo('#movieList');
}

 

jQuery Templates in Action

A sample application that I created to demonstrate jQuery Templates in action can be downloaded here (it’s part of the sample code available with our jQuery and AJAX Programming training course). The sample app is an ASP.NET MVC 2 project named “Order Up” that leverages jQuery heavily and uses jQuery Templates to render 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(i,mmi) MainItems}}
                    <tr>
                        <td>
                            ${ mmi.Name } - ${ mmi.NumberOrdered } ordered at $ ${ mmi.Price } per item
                        </td>
                    </tr>
                {{/each}}
            {{/if}}
            <tr>
                <td>&nbsp;</td>
            </tr>   
            <tr>
                <td class="OrderHeader">Accessories Ordered</td>
            </tr> 
            {{if AccessoryItems.length==0}}
                <tr>
                    <td>No items selected</td>
                </tr>
            {{else}}
                {{each(i,ai) AccessoryItems}}
                    <tr>
                        <td>
                            ${ ai.Name  } - ${ ai.NumberOrdered } ordered at $ ${ ai.Price } per item
                        </td>
                    </tr>
                {{/each}}
            {{/if}}
            <tr>
                <td>&nbsp;</td>
            </tr>                          
        </tbody>
    </table>    
</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 jQuery Templates tmpl() function:

 

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 json = {
                   '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'      : GenerateJson('Main'),
                   'AccessoryItems' : GenerateJson('Accessory')
               };


       $('#OrderSummaryOutput').html('');
//jQuery template example $('#OrderSummaryTemplate').tmpl(json).appendTo('#OrderSummaryOutput'); }

If you’re working with dynamic web applications that leverage jQuery and AJAX you’ll find that the new jQuery Templates plug-in can significantly increase your productivity and eliminate a lot of code that you’d normally have to write. It’s a great tool to have in your jQuery arsenal!

 

 

Logo_702D5F60[1]

If you or your company is interested in training, consulting or mentoring on jQuery 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

  • Very nice article. It does feel a little like I may get carpal tunnel though these days.

  • Dan,

    I know you're just showing an example but do you not think it's strange to have a lot of the logic of working out values being received is in your Javascript code? Especially that it's pretty clear that you'd be charging the custom 0.05% on their total order ;)

    Cheers,

    ChrisNTR

  • Chris,

    Yep...I'm with you. If the logic was only performed on the client side that would definitely be strange. As you mentioned, it's just a sample though in this case to demonstrate templates. Normally I'd calculate everything on the server-side to get the official amounts and then use AJAX to send those specific values down to the client as JSON (and then render the template) or perform the calculations on the server as well (doing it twice in other words). &nbsp;I'm OK with performing calculations on the client-side for the speed benefit as long as everything is re-calculated on the server to ensure no one is hacking. As I'm sure you do, I never accept client-side calculations as the final word...too much that can go wrong there. If a hacker wanted to mess with that they'd only affect what they're seeing on the client-side and not the final order amounts. The final order amounts that would show once they click the Place Order button would be generated by the server - if it were a real application. :-)

    Dan

  • Do you know when the non beta version of the templates is going to be out? I am hesitant to use something that is beta in my code.

  • Kevin:

    I'm with you although I'll have to admit that I've been using a super early version of the templates that John Resig put together for over a year with no problems (that's not to say that the beta is perfect of course). I haven't heard when the final release will be unfortunately.

    Dan

  • JQuery AND Microsoft? God Help us... pretty soon we'll be asked to buy more half assed attempts at operating systems except this time on the client side..

  • Very nice explanation about JQuery templates.

  • "it's part of the sample code available with our jQuery and AJAX Programming training course"

    Do you have a link for that? The domain the lab files are stored on seems to be sadly out of date and a Google search for "jQuery and AJAX Programming" returns nothing applicable.

  • Thanks for the concise explanation, easy to follow and digest quickly.

    I could see myself working with this but my current workflow isn't too bad. I typically render my template markup (one or several sections) in a div with it's visibility style set to none. Upon successfully generating a json object (usually through $.ajax()), I plug in the values into asp.net label or literal controls that I placed in the markup, then show/hide the appropriate container divs.

    I work on asp.net apps, not MVC so I'm curious, is there a benefit to changing approaches in my case or maybe the flavor of asp.net doesn't matter? The main benefit I can see is that I'd have more files separated out which could help with organization much like web user controls (.ascx) files.

    David

Comments have been disabled for this content.