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.
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:
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.
Other Posts you might also like