There was some interesting discussion on
Matt Berseth‘s blog
recently, regarding methods for building and displaying markup on the
client side. Though I haven’t posted any examples here before,
rendering markup on the client is a technique that I use often and
recommend.
By sending only data to the client, you can profoundly reduce the
size of what you send and see a substantial increase in performance.
You also allow yourself the ability to easily add features like
light-weight sorting and paging on the client. This can not only
improve your users’ experience, but
reduce server load and bandwidth requirements.
To that end, I’m going to walk you through these four steps to effectively implementing
a client side Repeater, using
ASP.NET AJAX and
jQuery:
- Create an RSS Reader page method to return JSON data to the client.
- Call that page method with jQuery.
- Use the returned data to build a table on the client side.
- Improve upon the table creation with a templating plugin.
Creating an RSS reader page method
Because web browsers prohibit cross-domain AJAX functionality,
displaying items from an external RSS feed is a good real-world example.
To overcome this limitation, our first step is to write a local server
side proxy to relay that feed data to the client.
A web service or page method is ideal for this task. I would
typically use an ASMX web service, but let’s use a page method here.
It’s useful to illustrate how nearly interchangeable the two really are.
[WebMethod]
public static IEnumerable GetFeedburnerItems(int Count)
{
XDocument feedXML =
XDocument.Load("http://feeds.encosia.com/Encosia");
var feeds =
from feed in feedXML.Descendants("item")
select new
{
Date = DateTime.Parse(feed.Element("pubDate").Value)
.ToShortDateString(),
Title = feed.Element("title").Value,
Link = feed.Element("link").Value,
Description = feed.Element("description").Value
};
return feeds.Take(Count);
}
This page method uses LINQ to parse a few details out of the RSS
feed, create an anonymous type with that data, and then return a
collection of those anonymous types. By selecting only the data we’re
interested in on the server side, we can minimize the traffic between
client and server.
In response to my recent deferred content loading post, several of you asked how to limit the results, so I added that this time around. The
Take extension method performs this task for us.
Calling the page method with jQuery
On the client side, the first thing we need to do is initiate a
request to the page method. We’ll do this with jQuery’s ajax() method:
$(document).ready(function() {
$.ajax({
type: "POST",
url: "Default.aspx/GetFeedburnerItems",
// Pass the "Count" parameter, via JSON object.
data: "{'Count':'7'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
BuildTable(msg.d);
}
});
});
When the page loads, this function will perform an AJAX request to
our page method, requesting information on the first seven feed items.
When the response arrives, a JavaScript function will be called with the
response data.
If you are unfamiliar with this jQuery syntax, I have covered it in more detail in two previous posts:
Using jQuery to directly call ASP.NET AJAX page methods and
Three mistakes to avoid when using jQuery with ASP.NET AJAX.
Building and displaying the table
The page method’s JSON response is going to be similar to this:
[{"Date":"6/5/2008",
"Title":"3 mistakes to avoid when using jQuery with ASP.NET AJAX",
"Link":"http://encosia.com/2008/06/05/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/",
"Description":"Three common problems that I've seen when using jQuery with ASP.NET AJAX, their underlying causes, and simple solutions to them."},
{"Date":"5/29/2008",
"Title":"Using jQuery to directly call ASP.NET AJAX page methods",
"Link":"http://encosia.com/2008/05/29/using-jquery-to-directly-call-aspnet-ajax-page-methods/",
"Description":"An example of how to use jQuery to call an ASP.NET AJAX page method, without using a ScriptManager."}]
The anonymous type in our LINQ query comes through very nicely. The
properties that we designated in the page method become keys in an
associative array, making it easy for us to work with the data.
To build a table of this data on the client side, we can loop through
each element, construct an HTML string, and then assign that string to a
container’s innerHTML property:
function BuildTable(msg) {
var table = '<table><thead><tr><th>Date</th><th>Title</th><th>Excerpt</th></thead><tbody>';
for (var post in msg)
{
var row = '<tr>';
row += '<td>' + msg[post].Date + '</td>';
row += '<td><a href="' + msg[post].Link + '">' + msg[post].Title + '</a></td>';
row += '<td>' + msg[post].Description + '</td>';
row += '</tr>';
table += row;
}
table += '</tbody></table>';
$('#Container').html(table);
}
If you think that code looks
ugly, that’s
because it is.
While it works great, I would not recommend this implementation. It
will be difficult for you to maintain, and you’ll hope that anyone else
forced to maintain it
doesn’t know where you live.
Improving the situation with templating
The reason the former code is so ugly is that the presentation and
logic are not separated. Even though this is all on the client side,
separation of concerns is still an important goal to keep in mind.
To achieve separation, what we really need is a templating solution. As it turns out, there’s a great jQuery plugin called jTemplates, which is perfect for this application.
Using jTemplates, we can create a simple HTML template like this one:
<table>
<thead>
<tr>
<th>Date</th>
<th>Title</th>
<th>Excerpt</th>
</tr>
</thead>
<tbody>
{#foreach $T.d as post}
<tr>
<td>{$T.post.Date}</td>
<td><a href="{$T.post.Link}">{$T.post.Title}</a></td>
<td>{$T.post.Description}</td>
</tr>
{#/for}
</tbody>
</table>
Since we’re focusing on disentanglement, this template belongs
separate file. If we embedded in our JavaScript, we’d be taking two
steps forward and one step back. In this case, I saved it as
RSSTable.htm.
Note: I originally used the suffix .tpl for templates, but found
that some versions of IIS will block access to them unless *.tpl is
explicitly added as a valid filetype.
With the HTML template created, we can use a couple of jTemplates’
methods to apply the template to the container and then render the page
method’s return.
function ApplyTemplate(msg) {
// This method loads the HTML template and
// prepares the container div to accept data.
$('#Container').setTemplateURL('RSSTable.htm');
// This method applies the JSON array to the
// container's template and renders it.
$('#Container').processTemplate(msg);
}
The rendered result will be identical to the previous, manual method, but the code is
much cleaner. At this level of abstraction, I consider this method to be very similar to a client side Repeater.
Conclusion
This is a powerful optimization technique.
Identifying overburdened UpdatePanels and replacing them with something like this yields massive performance increases. An order of magnitude isn’t uncommon, in my experience.
One of my sites,
WinScrabble.com,
was suffering from poor performance due to its primary feature relying
on partial postbacks. Because the page was simple and had ViewState
disabled, I thought the UpdatePanel would have a relatively minimal
impact.
You wouldn’t believe just how wrong I was.
When I removed the UpdatePanel and replaced it with this technique,
changing none of the underlying algorithms, requests for 7-8 letter
searches ran
over 400% faster. I have no doubt that you’ll be able to realize similar improvements in your projects.
In upcoming posts, I’ll go into detail about how to add some client side embellishments: Client side
sorting, light-weight
paging, and using
CSS to improve upon the presentation. So, if you aren’t already subscribed to receive updates via RSS or Email, be sure to do so today.
Download Source From:
http://encosia.com/2008/06/26/use-jquery-and-aspnet-ajax-to-build-a-client-side-repeater/