Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Client Templating with jQuery


:P
On this page:

jQuery makes it pretty easy to manipulate client side elements, so it’s not all that often that I think about using something like client side templating to get content loaded into pages. However, recently I have been working on a few apps that had fairly complex list based layouts and it started getting  tedious to use manual code to update all the items. Further doing it by hand can be brittle if your layout changes as you have to keep the layout and the update code in sync.

Templating can address this problem by letting you use templates that describe your output with ‘holes’ that are filled in with data when the template is processed. Templating is a good solution in a few scenarios:

  • Loading all data from the server especially in rich list displays
  • Adding or updating new items in lists
  • Anywhere you need to add new complex content to the page
  • Anything that requires client side HTML rendering

All of these scenarios have in common that new items are created and these items are injected into the page from the client.

‘Manual’ Templating

Templating can take many forms actually and it may not even require an official templating engine to feed data into the page. For example in a number of applications I’ve been using hidden elements that serve as a template into which data is then filled manually via jQuery code. Take this layout for example:

TemplateManual

which is filled from a ‘manual’ template that looks like this:

<div id="StockItemTemplate" class="itemtemplate" style="display:none">
     <div class="stockicon"></div>
     <div class="itemtools">
         <a href='javascript:{}' class="hoverbutton" 
onclick="DeleteQuote(this.parentNode.parentNode,event);return false;">
<
img src="../images/remove.gif" /></a> </div> <div class="itemstockname"></div> <div class="itemdetail"> <table style="padding: 5px;"><tr> <td>Last Trade:</td> <td id="tdLastPrice" class="stockvaluecolumn"></td> <td>Qty:</td> <td id="tdLastQty" class="stockvaluecolumn"></td> <td>Holdings:</td> <td id="tdItemValue" class="stockvaluecolumn"></td> <td id="tdTradeDate" colspan="2"></td> </tr></table> </div> </div>

The ‘template’ is a single hidden element in the page that is the empty layout of each of the template items that is loaded without any data applied to it. When the items are loaded from the server via an AJAX callback an array of Stock items are retrieved and they are then merged via code that finds each element and assigns the value.

function LoadQuotes()
{
    if (!userToken)
        return;   // *** not logged in        
        
    proxy.invoke("GetPortfolioItems",
         {userToken: userToken },
         function( message ) {            
            $("#lstPortfolioContainer").empty();
            
            $.each( message.Items,function(i) 
            {
                var item = this;   // this is the iterated item!
                
                // *** Create a new node from the template by cloning
                var newEl = $("#StockItemTemplate").clone()
                                .attr("id",item.Pk + "_STOCK")
                                .fadeIn("slow");                       
                
                // *** dump the data into it
                UpdatePortfolioItem(newEl,item);                        
                
                // *** Append item to the list view container and hook up click event for detail
                newEl.click(function() { ShowStockEditWindow(newEl); } )    
                     .appendTo("#lstPortfolioContainer");
            });

            // *** Update totals    
            $("#spanPortfolioTotal").text( message.TotalValue.formatNumber("c") );
            $("#divPortfolioCount").text( message.TotalItems.formatNumber("f0") + " items");
         },
         OnPageError);
}
function UpdatePortfolioItem(jItem,stock)
{                    
    // *** Now fill in the stock data 
    jItem.find(".itemstockname").text(stock.Symbol + " - " + stock.Company);        
    jItem.find("#tdLastPrice").text(stock.LastPrice.toFixed(2));
    jItem.find("#tdLastQty").text(stock.Qty.toFixed(0));
    jItem.find("#tdItemValue").text(stock.ItemValue.formatNumber("c"));
    jItem.find("#tdTradeDate").text("as of: " + stock.LastDate.formatDate("MMM dd, hh:mmt") ); 
}

The manual templating works by cloning the template element, assigning a new ID to it, filling it with data and then injecting it into the document in the right place – in this case into the list container.

This is a code centric approach and it’s pretty straight forward albeit a bit tedious and as mentioned potentially brittle if the template is changed.

Copy and Fill Templating

A similar approach that doesn’t require a separate template can be used if you need to add or update items in a list. Rather than cloning an empty template that is separately loaded into the page (and which some Html purists would complain about for document clarity) you can pick up an existing item on the page and duplicate it.

So in the example above instead of cloning a template I can select the first div tag and clone it:

var newEl = $("#lstPortfolioContainer>div:first-child").clone()
                .attr("id", item.Pk + "_STOCK");
     

This works as long you start off with existing content and you’re guaranteed that SOME content exists to clone from.

In the example above this wouldn’t work because the list renders initially empty and is filled from the client, but the copy and fill can work well in forms where you add new items or update existing ones and avoids any template embedding into the page. This can be especially useful for ASP.NET applications that fill lists with data server side and you only need to update or add items.

Although this approach doesn’t work for everything, when it does work it can be a great time saver because you don’t have to duplicate anything as you are simply reusing what was already rendered server side. I’ve used this frequently for client side updates of grids for example.

jTemplates

Manual embedding works, but as you can see it can be a bit tedious to find and update each item

There are few template engines available for jQuery and the one I’ve used for a while is jTemplates. jTemplates is fairly easy to use and it works reliably, although I have to say that I’m not a fan of the python like template syntax. But it works and is fairly powerful in terms of what you can accomplish. jTemplates work by taking the template and turning it into Javascript code that gets executed which means that template placeholder items can include expressions that reference other code.

Let’s look at another example. Here’s a form that’s my admin form my currently reading list on this blog. The list is originally rendered with a ListView control on the server. I can then go in and add or edit items which pops up ‘dialog’ ontop of the existing content:

jtemplate1

When the Save button is clicked the book list is updated or a new item added using jTemplates. The jTemplates template for an individual item looks like this:

<script type="text/html" id="item_template">    
    <div style="float: right;" id="divBookOptions">
        <a href="javascript:void(0);" onclick="removeBook(this,event);return false;" >
            <img border="0" src="../images/remove.gif" class="hoverbutton"/>
        </a>
        <br />
        <small>{$T.SortOrder}</small>
    </div>
    <img src="{$T.AmazonImage}" id="imgAmazon"/>
    <b><a href="{$T.AmazonUrl}" target="_blank" />{$T.Title}</b>
    <br/>
    <small>{$T.Author}</small>
    <br/>
    {#if $T.Highlight}<small><i>Highlighted</i></small>{#/if}   
</script>

Note the little trick of using a <script type=”text/html”> which is a little gimme from John Resig that allows hiding any markup in the document without interfering with HTML validators. The script can be accessed by its ID and the content retrieved using the .html():

var template = $("#item_template").html();

which gives you the template text. This is a useful trick for all sorts of things that you might want to embed into an HTML document as Data.

jTemplates uses python like syntax and the $T is a reference to the data item passed to the template. Data can be passed in as an object and you can reference the object’s properties off this $T data item.

Here’s the code that is used to save or add a new book to the list (both updating the server database and the list on screen):

function saveBook(ctl)
{
    var jItem = $(ctl);
   
    var book = activeBook;

   
// must assign the Pk reference
  
book.Pk = bookPk;
    if(bookPk < 1)
       bookPk = -1;
   
    book.Title = $("#"+ scriptVars.txtTitleId).val();
    book.Author = $("#"+ scriptVars.txtAuthorId).val();
    book.AmazonUrl =  $("#"+ scriptVars.txtAmazonUrlId).val();
    book.AmazonImage = $("#"+ scriptVars.txtAmazonImageId).val();
    book.SortOrder = $("#"+ scriptVars.txtSortOrderId).val();
    book.Highlight = $("#"+ scriptVars.chkHighlightId).attr("checked");
    book.Category = $("#"+ scriptVars.txtBookCategoryId).val();
   
    if(book.SortOrder)
        book.SortOrder = parseInt( book.SortOrder);
   
    showProgress();
    Proxy.SaveBook(book,
                   function(savedPk)
                   {
                        showProgress(true);
                        showStatus("Book has been saved.",5000);
                        panEditBook.hide();
                       
                        book.Pk = savedPk;
                        updateBook(book);
                   },
                   onPageError);
}

function updateBook(book)
{    
   var item = $(".bookitem[@tag=" +book.Pk +"]");    
      
   if (item.length < 1) {
        // create a new item wrapper to merge template inside of
        item = $("<div id='divBookItem' class='bookitem' tag='" + book.Pk + "' onclick='editBook(this);'></div>")
                    .appendTo("#divBookListWrapper");
    }
                     
    var template = $("#item_template").html();
    
    item.setTemplate(template);
    item.processTemplate(book);
}

The first function pulls the captured input values into a book object which is then submitted to the server. On the callback – when it worked – the book is updated and the list item is updated by calling the updateBook function. Inside that callback the existing item is selected or a new ‘wrapper’ created. Then the template is applied against that item and the parsed template replaces the inner content of the item selected. IOW, the template becomes the innerHTML.

The above is a simple example of a template that’s just a single item, but jTemplates also supports iteration over items as well as the ability to do some limited structured statements.

Here’s another example that’s used to render items from the Amazon Web Service which returns an array of Amazon items to the client:

<script type="text/html" id="amazon_item_template">    
{#foreach $T.Rows as row}
<div class="amazonitem" ondblclick="selectBook(this);" tag="{$T.row.Id}">
    <img src="{$T.row.SmallImageUrl}" style="float: left; margin-right: 10px;" />
    <div><b>{$T.row.Title}</b></div>
    <div><i>{$T.row.Publisher} &nbsp; ({$T.row.PublicationDate})</i></div>
    <small>{$T.row.Author}</small>
</div>
{#/for}
</script>

which is hooked up with code like this:

function showAmazonList()
{
    panBookList_DragBehavior.show();
    var search = $("#txtSearchBooks").val();
    if (!search)
       return;
                                        
    showProgress();        
    Proxy.GetAmazonItems( search,
                          $("#" + scriptVars.radSearchTypeId + " input:checked").val(),
                          $("#" + scriptVars.txtAmazonGroupId).val(),
                          function(matches) {
                                                    
                            showProgress(true);
                            bookList = matches;
                            var item = $("#divBookList_Content");                 
                  
                            item.setTemplate(  $("#amazon_item_template").html() );
                            item.processTemplate(matches);                            
                          },
                          onPageError);
}

Matches in this case is an array of AmazonLookupItems which looks like this:

[CallbackMethod]
public List<AmazonSearchResult> GetAmazonItems(string search, string type, string amazonGroup)
{
    AmazonLookup lookup = new AmazonLookup();
    List<AmazonSearchResult> result = lookup.SearchForBook(
            (type == "Title") ?
                Amazon.AmazonLookup.SearchCriteria.Title :
                Amazon.AmazonLookup.SearchCriteria.Author,
            search,
            amazonGroup);

    //result[0].

    return result;
}

The result is serialized as an array which is what

{#foreach $T.Rows as row}

{#/for}

iterates over.

You can also a see an example of the #if construct which allows you to conditionally display content.

{#if $T.Highlight}<small><i>Highlighted</i></small>{#/if}   

jTemplate supports only a few #directives including #if,#foreach,#for,#include,#param,#cycle which are few but admittedly enough for the most  common template scenarios.

I’ve used jTemplates in a few applications and it works well, but the syntax is really not to my liking. I also am not terribly fond of the way the plug-in works and how it assigns content as content. Making a tool like this a jQuery plug-in rather than a class that produces string output or at least allows options for that is one example of overemphasizing the jQuery metaphor.

John Resig’s Microtemplating engine

A couple of months ago John Resig posted a tiny little templating engine that is positively tiny. This engine is literally 20 lines of very terse (and yes obtuse) code. Heck I’ve looked at the regex expressions for a while now and I still have not quite figured out what it all does. It’s short enough I can post it here:

Updated code that fixes issue with single quotes (per Neil’s comment below):

// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function() {
    var cache = {};

    this.tmpl = function tmpl(str, data) {
        // Figure out if we're getting a template, or if we need to
        // load the template - and be sure to cache the result.
        var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :

        // Generate a reusable function that will serve as a template
        // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +

        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +

        // Convert the template into pure JavaScript
str.replace(/[\r\t\n]/g, " ") .replace(/'(?=[^%]*%>)/g,"\t") .split("'").join("\\'") .split("\t").join("'") .replace(/<%=(.+?)%>/g, "',$1,'") .split("<%").join("');") .split("%>").join("p.push('") + "');}return p.join('');");
        // Provide some basic currying to the user
        return data ? fn(data) : fn;
    };
})();

Basically it turns a template into a block of JavaScript code and then executes that code. The syntax is ASP style markup using <%= expression %> and <% codeblock %> syntax to handle code embedding.

What’s nice about this approach is that you can utilize any Javascript in the template and you’re not limited to just a few commands. The other thing that’s really nice is that it’s really compact – in fact I’ve integrated it into my own client library with a couple of small modifications. The main change I had to make for myself is that I can’t use <% %> because I’m using the script in another library where <% %> is always evaluated as server side script (note – ASP.NET is fine with the <% %> as long as you put it inside <script type="text/html"> which the ASP.NET parser is smart enough to leave alone. But even for ASP.NET I prefer to use a different script extension just to be clear. So I ended up using <# #> for delimiters. I also added some error handling so that maybe more error information can be seen if something goes wrong with the template.

Here’s my slightly modified and deconstructed version called parseTemplate:

Updated code that fixes issue with single quotes (per Neil’s comment below in italics):

var _tmplCache = {}
this.parseTemplate = function(str, data) {
    /// <summary>
    /// Client side template parser that uses &lt;#= #&gt; and &lt;# code #&gt; expressions.
    /// and # # code blocks for template expansion.
    /// NOTE: chokes on single quotes in the document in some situations
    ///       use &amp;rsquo; for literals in text and avoid any single quote
    ///       attribute delimiters.
    /// </summary>    
    /// <param name="str" type="string">The text of the template to expand</param>    
    /// <param name="data" type="var">
    /// Any data that is to be merged. Pass an object and
    /// that object's properties are visible as variables.
    /// </param>    
    /// <returns type="string" />  
    var err = "";
    try {
        var func = _tmplCache[str];
        if (!func) {
            var strFunc =
            "var p=[],print=function(){p.push.apply(p,arguments);};" +
                        "with(obj){p.push('" +
            //                        str
            //                  .replace(/[\r\t\n]/g, " ")
            //                  .split("<#").join("\t")
            //                  .replace(/((^|#>)[^\t]*)'/g, "$1\r")
            //                  .replace(/\t=(.*?)#>/g, "',$1,'")
            //                  .split("\t").join("');")
            //                  .split("#>").join("p.push('")
            //                  .split("\r").join("\\'") + "');}return p.join('');";

            str.replace(/[\r\t\n]/g, " ")
               .replace(/'(?=[^#]*#>)/g, "\t")
               .split("'").join("\\'")
               .split("\t").join("'")
               .replace(/<#=(.+?)#>/g, "',$1,'")
               .split("<#").join("');")
               .split("#>").join("p.push('")
               + "');}return p.join('');";

            //alert(strFunc);
            func = new Function("obj", strFunc);
            _tmplCache[str] = func;
        }
        return func(data);
    } catch (e) { err = e.message; }
    return "< # ERROR: " + err.htmlEncode() + " # >";
}

If you want to see what the generated code looks like uncomment the alert line – you’ll find that the template is deconstructed into a string of Javascript code that is then created as function and executed with the data parameter as an argument. The data parameter becomes the context of the call so that the properties of the object are available.

This is sort of the same approach like WCF uses of a ‘wrapped’ object  - to pass multiple parameters you can simply create a map:

parseTemplate($("#ItemTemplate").html(),
{ name: "rick", address: { street: "32 kaiea", city: "paia"} } );

where a template might be:

<script id="ItemTemplate" type="text/html">
 <div>
    <div><#= name #></div>
    <div><#= address.street #></div>
 </div>
</script>

You can also loop through a list of items by using code blocks. Imagine you got a list of stocks returned as an array as I showed earlier. You can then do:

var s = parseTemplate($("#StockListTemplate").html(), { stocks: message.listresult.Rows } );

which is then applied against this template

 <script id="StockListTemplate" type="text/html">
 <# for(var i=0; i < stocks.length; i++)     
{
var stock = stocks[i]; #> <div> <div><#= stock.company #> ( <#= stock.symbol #>)</div> <div><#= stock.lastprice.formatNumber("c") #></div> </div> <# } #> </script>

Effectively any child properties of the passed object are available as variables in the template courtesy of the with() construct in the generated Javascript code.

Personally I prefer to do scripting this way to what jTemplates does simply because you effectively have access to full Javascript functionality in the template. It’s also a more familiar approach if you’ve used any sort of ASP.NET scripting.

To put this in perspective here’s the first example where I manually loaded up the stock template replaced with the parseTemplate approach. In this example I use a single item template and use code to loop through list rather than having the template do it

The following is a script template similar to the stock template in the first example:

<script id="ItemTemplate" type="text/html">
<# for(var i=0; i < stocks.length; i++)     
   {         
     var stock = stocks[i]; 
 #>
 <div id="<#= stock.pk #>_STOCK" class="itemtemplate" style="display:none" onclick="ShowStockEditWindow(this);">
     <div class="stockicon"></div>
     <div class="itemtools">
         <a href="javascript:{}" class="hoverbutton" onclick="DeleteQuote(this.parentNode.parentNode,event);return false;">
         <img src="../images/remove.gif" /></a>
     </div>
     <div class="itemstockname"><#= stock.symbol #> - <#= stock.company #></div>        
     <div class="itemdetail">
         <table style="padding: 5px;"><tr>
             <td>Last Trade:</td>
             <td id="tdLastPrice" class="stockvaluecolumn"><#= stock.lastprice.toFixed(2) #></td>
             <td>Qty:</td>
             <td id="tdLastQty" class="stockvaluecolumn"><#= stock.qty #></td>
             <td>Holdings:</td>
             <td id="tdItemValue" class="stockvaluecolumn"><#= stock.itemvalue.formatNumber("c") #></td>                
             <td id="tdTradeDate" colspan="2"><#= stock.lastdate.formatDate("MMM dd, hh:mmt") #></td>
         </tr></table>
     </div>
</div>
<# } #>
</script>

In this example the template is loaded either individually or  updated in a loop to load all quotes:

function LoadQuotes()
{
    Proxy.callMethod("GetPortfolioItems",
         [],
         function(message) {
             $("#lstPortfolioContainer").empty();

             if (!message)
                 return;

             if (message.listresult) {

                 // Parse template with stock rows as array input
                 var html = parseTemplate($("#ItemTemplate").html(), 
                                         { stocks: message.listresult.Rows });
                 $(html).fadeIn("slow")
                        .appendTo("#lstPortfolioContainer");
             }

             // *** Update totals    
             $("#spanPortfolioTotal").text(message.totalvalue.formatNumber("c"));
             $("#divPortfolioCount").text(message.totalitems.formatNumber("f0") + " items");
         },
         OnPageError);
}

As you can see the Javascript code has been reduced significantly and the template – to me at least – is very easy to parse understand modify.

A problem with Single Quotes

As nice as the MicroTemplating engine is there is one problem: The parser has problems with templates that contain single quotes as literal text in some cases. The RegEx expression tries to allow for single quotes and it does in some cases work. But if you use single quotes to wrap attribute values the parser will fail with an ugly string error in the parseTemplate function because the single quote will end up as the delimiter for the function string resulting in invalid Javascript code to evaluate.

While this isn’t a big issue since it should be easy to avoid single quotes in markup and you can use &rsqutoe; for quote literals in HTML markup it’s still a bit inconsistent.

Updated code that fixes issue with single quotes (per Neil’s comment below)

Other Javascript Templating

Microsoft is also at it again as well with a new client template  engine slated for Version 4.0 of ASP.NET. MS  originally had client side templates in ATLAS which were later pulled – a good thing this didn’t make it because the XML based markup script was painful to work with with a hideous repetitious and self referencing model that was confusing as hell. The new template engine looks a lot cleaner and is bound and generally follows the same principles that I’ve shown above with jTemplates or the John Resig’s MicroTemplate parser.

Dave Ward has a great blog post with a sample that shows the basics of client templates and Bertrand also has an article on these templates in MSDN in the current issue. BTW, if you like the jQuery content here make sure you also subscribe to Dave’s RSS feed – he’s got some kick ass content on jQuery and ASP.NET.

I haven’t had a chance to look at this stuff other than reading through the articles. While I think this stuff looks very promising I can’t I’m too excited about it – mainly because it still relies on the rest of the Microsoft Client Library. Just to do scripting that’s a bit much of a hit especially when I already have alternatives in this space. But if you’re already using ASP.NET AJAX then the new features are a natural path to take.

Client Templating – Great for pure Client Implementations

I’m glad to see that there are a few templating solutions available. Templating makes creating pure client interfacing much easier and it brings some of the concepts that you might be familiar with ASP.NET server applications closer to home. After all things like the ListView, Repeater and other repeating controls are essentially template engines and many similar concepts are used in the client engines. Templates make it possible to let you render rich UI on the client without server markup and yet still let you keep the markup in one place (the template) and even lets you edit the content in your favorite designer if you choose to place the template into a regular document element that maybe is merely invisible.

Personally I like the approach of the MicroTemplate best because it’s dead simple and hits the right notes for me. I don’t need a fancy templating language if I can use JavaScript in my template to perform simple structural blocks and looping. No need to learn some funky template markup language but rather use what you know.

Posted in jQuery  JavaScript  

The Voices of Reason


 

Stephen
October 13, 2008

# re: Client Templating with jQuery

Just to provide yet another option for others to look into

jQuery "Chain"

http://ajaxian.com/archives/chainjs-jquery-data-binding-service

I've been using it for a few weeks and dig it

OecherCoder Blog
October 14, 2008

# Aktuelle Links

<p>&#160;Folgende Artikel sind spannender Lesestoff, zum Nachschlagen oder Lernen</p> ...

Elijah Manor
October 14, 2008

# re: Client Templating with jQuery

Good stuff! I can see myself doing some of this stuff now that I'm getting deep into ASP.NET MVC.

Aaron Jensen
October 14, 2008

# re: Client Templating with jQuery

Look into spark.. spark provides a story for a single template that can be rendered on either the server or the client. *that* is power.

Chris Haines
October 15, 2008

# re: Client Templating with jQuery

Have you tried using <#= "'" #> for the single quote issue?

(That's a single quote inside double quotes)

little p
October 15, 2008

# re: Client Templating with jQuery

Just a word of warning, W3C standards state that the element attribute 'id' should be unique within a document. Your templating example uses ids and that could potentially lead to problems depending on how various browsers handle cloned templates with conflicting ids. Since you're passing around a jquery object referencing a cloned instance you should be able to use class names instead.

Stephen
October 15, 2008

# re: Client Templating with jQuery

First off, thanks for the blog. Long time reader, I enjoy your approaches to problems.

If you're looking for a good, natural client-side templating solution check out:

http://jsonfx.net

It supports a standard ASP/JSP style syntax and compiles the template to standard JavaScript at *build-time*, meaning that the template parsing doesn't need to occur in the browser. It is fast and allows a familiar syntax without jumping through a lot of odd hoops.

JsonFx can be used piece-wise or as a complete Ajax solution solving many other problems like JSON-RPC, CSS/JS compaction and client-side localization.

Check out the demonstration at: http://starterkit.jsonfx.net

Asheesh Soni
October 15, 2008

# re: Client Templating with jQuery

Last attempt....

Great article (as always!)
Personally, I'd rather wait for ASP.Net Ajax 4.0 RTW before using client templates (<a href="http://ajaxpatterns.org/Browser-Side_Templating">The Browser-Side Templating Pattern</a>) in production websites.
Meanwhile, I am using a modified version of <a href="http://weblogs.asp.net/scottgu/archive/2006/10/22/Tip_2F00_Trick_3A00_-Cool-UI-Templating-Technique-to-use-with-ASP.NET-AJAX-for-non_2D00_UpdatePanel-scenarios.aspx">ScottGu's cool UI templating</a>. (i.e. using the <a href="http://ajaxpatterns.org/HTML_Message">HTML Message Pattern</a>)
My modifications allow me to use javascript / jquery / toolkit extenders in my dynamically injected user controls.
The good thing about it is that I can (and do) use the same user controls in server side aspx pages, with full visual support, intellisense etc. from Visual Studio.
I'll blog about this implementation on my <a href="http://asheeshsoni.blogspot.com/">.Net Development Blog</a> when I get some time.

For more info, refer to the following MSDN Cutting Edge articles:
<a href="http://msdn.microsoft.com/en-au/magazine/cc546561.aspx">ASP.NET AJAX And Client-Side Templates by Dino Esposito</a>
<a href="http://msdn.microsoft.com/en-au/magazine/cc699560.aspx">The HTML Message Pattern by Dino Esposito</a>

Cheers
Asheesh Soni

Derek
October 26, 2008

# re: Client Templating with jQuery

Hi Rick,

One of the data elements I am inserting into a template (eg. <div>{$T.commentText}</div>) is actual html but it being rendered as the html text instead of the representation of the html, if you know what i'm missing i'd really appreciate your help.

Derek
October 26, 2008

# re: Client Templating with jQuery

Hi again,

Its OK I found the answer in one of Dave Ward's posts.

For anyone else having the same problem it is because jTemplates has HTML filtering on by default, to disable it use:

.setTemplateURL("jTemplates/CommentListTemplate.htm", null, { filter_data: false });

Not trying to steal your thunder Rick, I just don't want to be one of those people who say "I fixed it" and then leave the rest of the world wondering.

Neil
November 05, 2008

# re: Client Templating with jQuery

Rick - regarding John Resig's Simple JavaScript Templating, I fixed the multiple quotes issue by rewriting the splits and replaces. Here's the code, which is a drop in replacement for the "Convert the template..." section. Lines 2-4 are the only fundamentally different lines.

str.replace(/[\r\t\n]/g, " ")
   .replace(/'(?=[^%]*%>)/g,"\t")
   .split("'").join("\\'")
   .split("\t").join("'")
   .replace(/<%=(.+?)%>/g, "',$1,'")
   .split("<%").join("');")
   .split("%>").join("p.push('")
   + "');}return p.join('');");

Chad
November 21, 2008

# re: Client Templating with jQuery

Great article. I've learned a tremendous amount from your site and appreciate all the time and energy you've invested in helping the community. Thanks!

Mason Houtz
December 13, 2008

# re: Client Templating with jQuery

This is great.

I've been doing this kind of thing for about 4 years with a simple jQuery extension I wrote one day and never really looked at again. I like some of the ideas surfaced here in the various implementations you've demonstrated though.

One thing that's really important in any templating engine (I believe) is the ability to associate non-markup data with dom elements along the way. For example, I'm typically stepping through an object hierarchy and I'll need to associate some node of that object structure with some rendered element so that I can have some data to work with later when the user clicks on the doodad I just renderd. A link, for instance.

Making sure that your templating engine offers you the chance to assign arbitrary non-rendered data onto individual rendered "nodes" (via the .data() jquery method or some other similar approach) is an important process that a lot of straight text-substitutions can't offer. (Sure you could embed IDs into custom attributes, but what if your data is uglier than just a number?)

Anyway, here's my old templating scheme if anybody's interested. If anything, it's a lot simpler than some of the solutions you've presented here, so maybe some of your audience can use it as a stepping-off point to make it all fancy-like for their individual needs.

Thanks for a great blog.

Plugin
http://nerdinacan.com/resources/js/jquery.stamp.js

Dependencies (wish I could remember who I stole this from)
http://nerdinacan.com/resources/js/type.of.js


Mason Houtz

Aashish Gupta
February 04, 2009

# re: Client Templating with jQuery

Using jTemplates in my template code i have attached a button with every row.Now i want to attach some click event handler to this button using jquery.So where will i attach this handler in template htm or tpl page or in the main aspx page on which this template is rendred.
If i write it on the main aspx page while the jquery is loaded there is no button.The template only loads when i click a certain link then the webmethod is called and then this template is rendered but the event is not attached to the templates button which is rendered in every row

Aashish Gupta
February 28, 2009

# re: Client Templating with jQuery

Mission Accomplished
Here is a grid with full functionalities i.e paging sorting and filtering
http://smallworkarounds.blogspot.com/2009/02/jquery-aspnet-how-to-implement.html

Kev
April 20, 2009

# re: Client Templating with jQuery

Hi there,

You mention that the .net parser ignores <% %> inside the text/html block. This doesn't happen for me. It picks up on undeclared server variables, for example. So in this case below, there's a server error of "The name 'i' does not exist in the current context". I know you mention that a hash (#) can be used instead, but why doesn't "%" work as suggested above? VS2008 is being used. Thanks for reading. Nice work.

<script type="text/html" id="thumb_template">
Line 13:      <ul>
Line 14:      <% for(i=0;i<items.length;i++) { %>
Line 15:           <li><img src="<%=items[i].media.m%>" /></li>
Line 16:      <% } %>

Chris Kapilla
April 27, 2009

# re: Client Templating with jQuery

Hi Rick,

returning to this post after a number of months, and found it very useful -- thanks. I was however, puzzled by your two different comments

"Personally I prefer to do scripting this way to what jTemplates does simply because you effectively have access to full Javascript functionality in the template."

and

"Personally I like the approach of the MicroTemplate best because it’s dead simple and hits the right notes for me."

Care to elaborate on which one you _really_ prefer?

thanks

Chris K

Rick Strahl
April 27, 2009

# re: Client Templating with jQuery

@Chris - I like both approaches actually, but I prefer the micro-template because it's just JavaScript tags and behaves more like you'd expect a simple ASP like template engine to work. Plus it's tiny so it's easy to integrate into other tools and I have integrated it into my core client library so it's always available.

So to answer your question: It's clearly micro-templating. :-}

Chris Kapilla
May 05, 2009

# re: Client Templating with jQuery

Rick,

I was glad to see that you are monitoring these older postings and still responding to them. I have implemented your version of the micro-template and found it works perfectly for me -- and best of all thanks to your 'elucidations' I even understand how it works now -- which upon first glance I didn't think was possible! :-)

I have one issue though -- I don't seem to be able to add any JQuery plug-in event handlers to DOM elements that are created via a template. Is there something special one needs to do in order to make that work?

thanks in advance,

Chris K

Rick Strahl
May 05, 2009

# re: Client Templating with jQuery

@Chris - I add event handler frequently and that does work for me, so I'm sure it's possible.

Here's an example:
http://www.west-wind.com/WestwindWebToolkit/samples/Ajax/AmazonBooks/BooksAdmin.aspx

You can look at the JavaScript code on that page and look at the UpdateBook method which creates a new item and then attaches click events.

Chris Kapilla
May 05, 2009

# re: Client Templating with jQuery

wow that was fast -- you are the best!

thanks for the link I will check it out. It's cool they way you make the different aspects of the code all easily viewable.

Strx
May 14, 2009

# re: Client Templating with jQuery


Mark
May 25, 2009

# re: Client Templating with jQuery

Rick,

Thanks for this post and your site. I just discovered it and am glad I did.

As a side note, I was investigating the micro-template code today and was puzzled by a portion of the following line:

"var p=[],print=function(){p.push.apply(p,arguments);};" +

I went to John's Resig's actual post to further investigate why the following was included?

print=function(){p.push.apply(p,arguments);};

The method is never called within the function. So I have removed it and the code runs successfully. (A debug function I assume).Have you discovered a need for this?
And thanks for adding the error handling portion.

Mark

Brian
June 22, 2009

# re: Client Templating with jQuery

Just curious, but has anyone managed to find a way to do includes for the templates so that they can be reused on other pages?

I tried moving the template out to another file and including it via the src attribute on the script tag but that didn't work out.

Thanks in advance.

Warren
June 27, 2009

# re: Client Templating with jQuery

I copied and paste the basic example using the John Resig example you modified but I can't seem to render the contents inside the <script type="text/html" id="ItemTemplate">. What am I missing?

Even without running the parseTemplate, I can't seem to show any information inside the script tag. e.g <script type="text/html" id="ItemTemplate">Hello World</script> inside the <body></body> but nothing is being displayed.

Rick Strahl
June 27, 2009

# re: Client Templating with jQuery

The template by itself doesn't do anything. It's basically hidden text. You have to parse the template with parseTemplate and the assign the result into the document.

Warren
June 28, 2009

# re: Client Templating with jQuery

Thanks Rick, it makes sense. It was just as simple as loading it into a document.

vic
August 04, 2009

# re: Client Templating with jQuery

hey, this is a great article. i am looking for a templating solution that conforms to POSH html. i dont want any control mechanism in the templates , like if's or loops. The first example seems like it could conform to POSH html very well. the designer can design the html and css, and the programmer can do the js. i think my only question is with the UpdatePortfolioItem() function in the first example. this would imply what i would need a template update function for each type of template. do you think its possible that a function could run through the ids and get the corresponding value from the json data? that way one function could update any template???

vic
August 05, 2009

# re: Client Templating with jQuery

you could use something like this...


function updateTemplateDynamically(jItem, jsonData)
{
jItem.find(".updateMe").each(function()
{
var id = $(this).attr("id");
jItem.find("#" + id).text(jsonData[id]);
});
}



template would like like:


<div id="dynamicTestTemplate" class="itemtemplate" style="display:none">
<div class="updateMe" id="headline"></div>
<div class="updateMe" id="text"></div>
<div class="updateMe" id="OID"></div>
</div>

ae.fxx
September 08, 2009

# re: Client Templating with jQuery

Hey guys.

You might want to give http://aefxx.com/jquery-plugins/jqote/ a try.
It's John Resig's micro templating code ported to the jQuery framework.

The conversion part has been optimized (tiny gain of speed) and the ability to convert
multiple templates at once added.

jQote is a jQuery JavaScript templating plugin.

Rick Strahl
September 09, 2009

# re: Client Templating with jQuery

@ae.fxx - One problem with this is that it's using <% %> which will blow up in ASP.NET as the server parsing will process it. The one I have here will work with ASP.NET proper. Might be nice to add the delimiter string as an optional parameter to the jQuery plug-in.

Yuval Kaplan
November 06, 2009

# re: Client Templating with jQuery

Rick,

Firstly, great writing. No run around - we all want to get the job done and you always show us the way.

Secondly, I started using jTemplate but encountered a parsing problem that showed up on Firefox but on on IE8. It way be due to an error I have on the page that is not related to the script itself but the fact is it just wouldn't work.

I found the alternative by John Resig much more clear and less cryptic.

(I used your version as I don't like to confuse with the server tags)

Thanks for the great post!

Tomas Salfischberger
December 21, 2009

# re: Client Templating with jQuery

Great overview, nice to see some updates on the Micro-Templating idea. We use the Micro-Templating concept in our crm tool (Unfortunately only in Dutch: http://celerity-crm.nl/) for separating markup elements from Javascript code and re-use of some of the snippets.

To make this a little more maintainable I have combined John's original template idea with some simple configuration options to form a jQuery plugin. (And refactored it to be a little more verbose but more understandable to normal humans ;-))

The basic usage of the plugin is to create a template just like John does (or using a textarea if you like) and then update an elements contents like this:

$("#someElement").fromTemplate("templateId", dataset)


Where dataset is the data you want to expose to the template (usually the results from a JSON(P) call). The code is shared here: http://github.com/celerity/jQuery-fromTemplate-plugin feel free to improve / comment on it.

Sherry Ann
March 09, 2010

# re: Client Templating with jQuery

Hi Sir,

How do you format the date and money values inside the template/

Thanks,
Sherry

jq
March 16, 2010

# re: Client Templating with jQuery

returning to this post after a number of months ... Have you tried using <#= "'" #> for the single quote issue?

jhs
March 19, 2010

# re: Client Templating with jQuery

Here is a new jquery template plugin "YATE" that separates the template markup from the javascript data and logic pretty nicely: http://labs.mudynamics.com/2010/03/19/yate-javascript-templating-engine-for-agile-ui-development/

diyism
April 15, 2010

# re: Client Templating with jQuery

Give a try to the real "jquery micro template", it need no seperate template container:
http://plugins.jquery.com/project/micro_template

<div id="test1" class="users">
     <!for (var i=0;i&lt;users.length;++i)
           {!>
<div onMouseOver="/*=users[i].color?'this.style.color=\''+users[i].color+'\';':''*/" id="user_&lt;!=i!>" class='user'>Name:<a href="&lt;!=users[i].name!>"><!=users[i].name!></a></div>
           <!}
     !>
<pre>'somthing'
else ...</pre>
</div>
 
<script> 
var data1={users:[{name:'name1.1'},
                  {name:'name1.2', color:'yellow'}
                 ]
          };
$('#test1').drink(data1);
</script>

The Accidental Geek
May 24, 2010

# SocialScapes Twitter Widget

SocialScapes Twitter Widget

Ewan
July 27, 2010

# re: Client Templating with jQuery

Hi,

I'm guessing that this line:

.replace(/'(?=[^#]*#>)/g, "\t")

is supposed to temporarily remove apostrophes which are within <# #> blocks. But it blew up for me when I had a lone # in a code block. So I've replaced it for my own purposes with:

.replace(/'(?=(?:[^#]|#[^>])*#>)/g, "\t")

which seemed to help.

ismail codar
October 01, 2010

# re: Client Templating with jQuery

Recent simple and powerfull template engine is: http://github.com/ismail-codar/templatejs

Alexander
October 06, 2010

# re: Client Templating with jQuery

I really like John's micro templating approach and plan to use it. That it bombs out whenever the - probably not very experienced - template creator accesses a null object or non existing properties of the javascript object that is passed in is not optimal. The template creator should get some kind of feedback what error occured and where, so I changed the regular expression a little bit to cover this:

str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^#]*#>)/g, "\t")
.split("'").join("\\'")
.split("\t").join("'")
//.replace(/<#=(.+?)#>/g, "',$1,'")
.replace(/<#=(.+?)#>/g, "'); try { p.push($1); } catch(err) { p.push('Error: ', err.description); }; p.push('")
.split("<#").join("');")
.split("#>").join("p.push('")
+ "');}return p.join('');";

Now if the template writer would include something like <#= foo.bar #> and if foo is null it would show an error instead of bombing out completely.

Mike Edmunds
March 01, 2011

# re: Client Templating with jQuery

@Mark: Don't know if you're still following this, but...

The print() function lets you output content within an embedded <%...%> block.

One handy use (that I just ran into) is to call the tmpl() function from within a template -- nested template expansion. Example:

<script type="text/html" id="post_tmpl"> 
...
<% $.each(post.comments, function(i, comment){
   print( tmpl("comment_tmpl", comment) ); /* nested call to tmpl() */
}); %>
...
</script>


You could also code this with <%= instead of print(), but that requires multiple <% blocks, and makes (I think) for uglier code:

<script type="text/html" id="post_tmpl"> 
...
<% $.each(post.comments, function(i, comment){ %>
   <%= tmpl("comment_tmpl", comment) %>
<% }); %>
...
</script>


- Mike

Daniel
April 01, 2011

# re: Client Templating with jQuery

I know this is old, but it's friggin' brilliant! Many thanks for this post (and a lot of your others).

some internet guy
May 08, 2011

# re: Client Templating with jQuery

scripts like these makes JavaScript awesome! Thanks!
The best part is, it works without jQuery! (the title is a little bit confusing ..)


if you want to preserve whitespace in your template (ie: you're using < pre > or < textarea >)
change

.replace(/[\r\t\n]/g, " ")

into

.replace(/\t(?![^#]*#>)/g, "\\t") //match tabs outside <##> tags
.replace(/(\r?\n)(?![^#]*#>)/g, "\\n") //match newlines outside <##> tags
.replace(/[\r\t\n]/g, " ") //match whitespace inside <##> tags


cheers!

Darren Hevers
January 05, 2012

# re: Client Templating with jQuery

Hey Rick

Can you please explain how I can reference multiple templates from a master template.

Cheers

Darren

Rick Strahl
January 05, 2012

# re: Client Templating with jQuery

@Darren - The micro-template engine is pretty basic so it doesn’t directly support nested templates.

However, expressions and code blocks are JavaScript code so you can simply call ParseTemplate() from within the existing template. It just gen’s a string so this should work. I haven’t tried this myself, but this should work:

<#= ParseTemplate( $(“#TestTemplate”).html(),document) #>

Kernel James
February 21, 2012

# re: Client Templating with jQuery

I use the Distal template system http://code.google.com/p/distal

leo
August 15, 2012

# re: Client Templating with jQuery

I made a very nice templating engine based upon John's ideas:

http://2basix.nl/page.php?al=javascript-templating-engine

this one is even more lean then the original post from John..

Maybe you can check it out..

71104
June 27, 2013

# re: Client Templating with jQuery

There is also a Handlebars-based solution: http://71104.github.io/jquery-handlebars/

Disclaimer: I'm the author. :P

Terry Lin
May 17, 2015

# re: Client Templating with jQuery

Your script works good but I found a bug on your script
var new_html = parseTemplate($("#category-tpl").html(), {id: res[i].id, text: res[i].link} );

parseTemplate() can't parse something like res[i].id ( res.id is fine )

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024