Client Templating with jQuery

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:

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:

Note the little trick of using a

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 GetAmazonItems(string search, string type, string amazonGroup)
{
AmazonLookup lookup = new AmazonLookup();
List 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}Highlighted{#/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

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

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:

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

2 thoughts on “Client Templating with jQuery

  1. You really make it seem so easy with your presentation but I find this topic to be
    actually something that I think I would never understand.
    It seems too complicated and very broad for me. I am looking forward
    for your next post, I will try to get the hang of it!

  2. Hello there, There’s no doubt that your blog may be having browser compatibility problems.
    Whenever I take a look at your site in Safari, it looks fine but when opening in Internet Explorer, it has some overlapping issues.
    I just wanted to give you a quick heads up! Other than that, fantastic
    site!

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s