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:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Client Side Templating with jQuery


:P
On this page:

I'm working on my DevConnections demos for a couple of sessions. One of the examples is a stock REST service interface that provides client side display of stock portfolio data. When I first started putting this small demo together I started with a ListView control that rendered the portfolio data server side.

The display of the (incomplete) portfolio data is shown in the figure below.

Stockdisplay

The server side ListView code looks something like this:

<div id="lstPortfolioContainer" >
<asp:ListView runat="server" ID="lstPortfolio" ItemPlaceholderID="PortfolioItemTemplate">
<LayoutTemplate>
 <div id="PortfolioItemTemplate" runat="server" />    
</LayoutTemplate>
<ItemTemplate>
  <div id="<%# Eval("Pk") %>_Stock" class="itemtemplate">
    <div class="itemstockname"><%# Eval("Symbol") %> - <%# Eval("Company") %></div>
    <div class="itemdetail">
        <table style="padding: 5px;"><tr>
            <td>Last Trade:</td>
            <td id="tdLastPrice" class="stockvaluecolumn"><%# Eval("LastPrice","{0:f2}") %></td>
            <td>Qty:</td>
            <td id="tdLastQty" class="stockvaluecolumn"><%# Eval("Qty","{0:n0}") %></td>
            <td>Holdings:</td>
            <td id="tdItemValue" class="stockvaluecolumn"><%# Eval("ItemValue","{0:n}") %></td>
            <td colspan="2">as of: <%# Eval("LastDate","{0:MMM dd, hh:mmtt}").ToString() %></td>
        </tr></table>
    </div>
  </div>
</ItemTemplate>    
</asp:ListView>
</div>

And this works well enough for displaying the data.

But if you are building an AJAX centric UI having a server side list view is a little problematic - if you want to update the data rendered on the client it's difficult to retrieve say just a single item in the list and change it. One approach to use is partial rendering using something like UpdatePanel in ASP.NET AJAX or as I showed a few days ago by using selective control rendering using a RenderControl() or RenderUserControl() type approach. The problem with this is that every update requires sending down all the list data again and that in some cases you may loose vital information like the scroll state of the list for example.

Client Side UI creation?

Wouldn't it be nice if you could use a more modular approach from the client side and actually let you render the list or update individual items through client code?  If you've done any amount of client side Ajax coding I'm sure you've run into this dilemma where you need to decide what to render on the server and what to render on the client and where to put the UI manipulation code. Server side templating vs. client scripted manipulation.

Take the example above. If I wanted to add a new portfolio item to the list I can send back the data to the server and have the data return the entire list again as HTML. But this involves quite a bit of extra overhead - the server hit and presumably a page cycle plus going out to the database multiple times to retrieve the data. Realistically we're only changing one item in the list and only need to send the original data to the server and get back the updated data (in this case a full portfolio item) and then add it to the existing list.

So wouldn't it be nice if you could design your HTML in HTML Markup as usual and then just use client script code to  load in the data to the appropriate locations, in a sort of a templating mechanism?

It turns out with a few tricks and the cool features in jQuery it's actually quite easy to do this. I suppose it's not really templating in the sense that you won't have placeholders in the markup, but as we'll see jQuery's selectors make it very easy to pick out the areas in the form where to put the data.

The key to this approach is the ability to duplicate DOM elements. jQuery has a really nice .Clone() method that allows you to take an HTML fragment and basically copy it creating a new element tree with the exact same layout.It's actually quite easy to create a small block of markup into the page that acts as an input 'template'. So instead of the ListView I might have the following template HTML in the page:

<div id="StockItemTemplate" class="itemtemplate" style="display:none">
    <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>

Plain old HTML!

But if you look closely the naming and layout match exactly the the format of the ItemTemplate in the ListView. Now using jQuery I can add new items to the list pretty easily.

In this example I'll add a new Portfolio item to the page by popping up a window to enter the symbol and qty:

AddStock

The add button click  then initiates an AJAX callback that goes out to the server, which in turn looks up the stock, and creates a new portfolio item that is returned to the client as a complex object in a JSON callback. The result is then added to the existing list using the same formatting. Here's the full client side JavaScript code for this full operation:

// *** Display the Window 
function ShowStockEditWindow()
{
    // *** Make it visible
    $get("StockEditWindow").style.display="block";        
}
// *** Add Portfolio Item button click
function AddNewQuoteToPortfolio()
{    
    var symbol = $get("txtPortfolioSymbol").value;
    var qty = $get("txtPortfolioQty").value;    
    var token = "";
 
    MsAjaxStockService.StockService.AddPortfolioItem(symbol,qty,token,AddNewQuoteToPortfolio_Callback,OnPageError,OnPageError);
}
// *** Callback for add operation
function AddNewQuoteToPortfolio_Callback(result)
{
    if (result == null)
    {
        showStatus("Unable to add new portfolio item.",5000);
        return;
    }    
 
    // *** Clone node assign ID attribute and fade in
    var newEl = $("#StockItemTemplate").clone().attr("id",result.Pk + "_STOCK").fadeIn("slow");
 
    // *** Now fill in the stock data 
    newEl.find(".itemstockname").text(result.Company);        
    newEl.find("#tdLastPrice").text(result.LastPrice.toFixed(2));
    newEl.find("#tdLastQty").text(result.Qty.toFixed(0));
    newEl.find("#tdItemValue").text(result.ItemValue.formatNumber("n"));
    newEl.find("#tdTradeDate").text("as of: " + result.LastDate.formatDate("MMM dd, hh:mmt") );    
 
    //newEl.insertAfter("#lstPortfolioContainer .itemtemplate:last");
    newEl.appendTo("#lstPortfolioContainer");
 
    $("#StockEditWindow").fadeOut();
}

So the key is in the callback handler method. The method receives a StockPortfolioItem object which has properties like Symbol, LastPrice, LastDate, NetChange etc. It's basically stock information plus the holdings info for that security. The server side code created a new portfolio item, did a look up for the latest stock data on Yahoo, then saves the new item to the database and returns it back to the client for display.

The slick part is the block of jQuery code on the bottom which basically amounts to templating:

// *** Clone node assign ID attribute and fade in
var newEl = $("#StockItemTemplate").clone().attr("id",result.Pk + "_STOCK").fadeIn("slow");

This code takes the HTML template I showed  earlier and clones it, overrides the ID attribute (so we can find it later by its PortfolioItem Pk) and then displayed with an effect. This effectively creates a new DOM element.

The code that follows then updates the fields displayed with the values from the result object. Note that the block of HTML generated includes duplicated ids for each block. But using jQuery's selectors it's easy to pick out the RIGHT child Id using the .find() sub querying method to select each element (in this case the TDs). This replacement is the most tedious part of the process as you have to match each element to the data value to assign. Assuming your CSS styles are set up properly for each you can just write the data into the template. As long as your HTML 'template' has clearly accessible elements jQuery can find them and you can write the data into them easily.

Finally, once the item has been completely updated it's attached to the bottom of the list with:

newEl.appendTo("#lstPortfolioContainer");

If you look back on the original list image I showed and you can see the ListView generated items as the first 2 items and the client added items for the remaining items. There's a slight difference between my date formatting routine and the .NET date formatting which capitalizes the AM/PM designator. Otherwise the layout and display is the same.

Now I can - and in fact will later - do the intial rendering in this same way, so that an initial callback retrieves maybe a list of StockPortfolioItems and then 'binds' the entire list via this same JavaScript templating approach.

The real value for this sort of thing though comes for edits and adds as I showed. By using the template approach you can basically update one item at a time rather than having to completely re-render the list.

I've used this 'templating' approach for a number of applications and it works really well and doesn't require a lot of code. You still get the ability to do your layout and design in HTML, but you also get a lot of control over the rendering of the template from client code. jQuery makes this sort of thing very easy.

Posted in ASP.NET  AJAX  jQuery  

The Voices of Reason


 

Matt Brooks
April 03, 2008

# re: Client Side Templating with jQuery

I'm not sure what Alan means but is it valid to have duplicate ID attribute values within a single HTML document? As you say, jQuery still allows us to reference the correct element by ID - and so does ASP.NET AJAX via the $get(id, parentElem) method.

John S.
April 03, 2008

# re: Client Side Templating with jQuery

@Matt Brooks - It's not valid, but it does work. If you want it to be valid, just change those IDs to classes.

The clone() templating method is brilliant. I've just been using string concatenation but no longer! I tried the FlyDOM plugin as well but I couldn't figure out how to get it to nest properly (I could only build one child at a time).

William Plander
April 03, 2008

# re: Client Side Templating with jQuery

You've been messing with these little Stock Portfolio samples for years....think you'd be able to whip together one mean little Portfolio Tracking website by now. <grin>

Rick Strahl
April 03, 2008

# re: Client Side Templating with jQuery

@Matt - it's not valid but it works, and a HTML validator won't catch this because the original HTML that loads doesn't actually cotain the duplicate ids if you add them with script... As John says if you're worried about validity classes also work, but class selection is significantly slower than ID selection so I tend to use IDs since it works in all major browsers.

@Bill - yeah those stock apps, they make good examples. Everybody understands them and they are easy enough to build samples around that don't get too complex to demonstrate and explain. Or maybe it's just because of working with you and all that stock related stuff we've worked with over the years <g>.

Denny Ferrassoli
April 03, 2008

# re: Client Side Templating with jQuery

jQuery has something called jTemplates which is pretty cool: http://jtemplates.tpython.com/

Jim
April 04, 2008

# re: Client Side Templating with jQuery

I'm wondering how you generate those formatted code snippets that you put in your blog posts. I've used things like GeSHi for wordpress, but it doesn't format exactly like Visual Studio. Are you pasting code from VS into your Html Help Builder and then copying that Html to your blog posts? I downloaded your app to see if you could do that, but just wondering if you have a plug-in or something that is allowing you to format these posts in a more automated fashion.

Thanks

Rick Strahl
April 04, 2008

# re: Client Side Templating with jQuery

No I use LiveWriter and a plug in I created based on J. Leigh's Visual Studio plug-in that picks up HTML from the VS clipboard:

http://www.west-wind.com/WebLog/posts/221134.aspx

You should look into using Live Writer - I think it can post to WordPress. There are a number of add-ins available for VS pasting...

Jim
April 04, 2008

# re: Client Side Templating with jQuery

Thanks, Rick. Just what I've been looking for.

laimis
April 05, 2008

# re: Client Side Templating with jQuery

If you have some time check out trimpath project at http://code.google.com/p/trimpath/ and in particular their javascript template library at http://code.google.com/p/trimpath/wiki/JavaScriptTemplates . I used to do similar to what you are doing with loading a template div and then cloning it and setting values that way. I think using javascript templates is even more flexible and most important very easy to maintain. Try it out, once I started using I cannot go back.

Sam Mueller
October 23, 2008

# Client side templates using ASP.NET, JQuery, Chain.js, and TaffyDB

Client side templates using ASP.NET, JQuery, Chain.js, and TaffyDB

DotNetShoutout
November 19, 2008

# Client Side Templating with jQuery

Your Story is Submitted - Trackback from DotNetShoutout

KierenH
January 22, 2009

# re: Client Side Templating with jQuery

Rick,
Almost a year since you wrote this post, and MS coming out with Ajax client-size templating.
Do you still recommend this approach? I like it :)
Kieren - AUS

MangoBoy
November 08, 2009

# re: Client Side Templating with jQuery

This is a very nice example of simple templating. I come from a PHP background and have used templating engines such as Smarty, but I found that this just adds another level of complexity and slows the development process even more. If you add the time required to get aquainted with the various frameworks that can be used on a web application project it just starts to get silly - for example on a PHP web application you might need to be familiar with HTML, CSS, vanilla PHP code, Code Igniter PHP Framework, HTML DOM, Vanilla Javascript, jQuery Javascript Library and perhaps X Javascript templating engine - crikey that's a big ask of a developer to be able to not just be adequate but to be excellent at coding and debugging these various aspects of the system. I do of course think there are cases for using a full fledged templating engine, but this example shows a sensible solution that will fit the vast majority of development projects and allow the developers to concentrate their efforts on the quality of their code, the usability of the system and minimise the number of bugs too.

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