Nearly 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:
- http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js
- http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js
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}} |
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}} |
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}} |
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> |
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 ourjQuery 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:
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"> </td> </tr> <tr> <td colspan="2">Will be charged to your credit card ending with ${ CreditCard }</td> </tr> </table> </td> </tr> <tr> <td> </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> </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> </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> </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!
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.