Note: This post is part of a long-running series of posts covering the union of jQuery and ASP.NET: jQuery for the ASP.NET Developer.

Topics in this series range all the way from using jQuery to enhance UpdatePanels to using jQuery up to completely manage rendering and interaction in the browser with ASP.NET only acting as a backend API. If the post you're viewing now is something that interests you, be sure to check out the rest of the posts in this series.

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.

Try it for yourself: download the source

Download Source: client-repeater.zip (25kb)