Easily build powerful client-side AJAX paging, using jQuery

Posted on August 20, 2008 by Dave Ward.
Categories: ASP.NET, Contributors.

A book's pages blowing in the wind

When I suggest that developers consider using web services and a more client-centric approach to solve their UpdatePanel performance problems, the lack of paging is often their first objection.

Conventional ASP.NET wisdom seems to hold that the GridView/UpdatePanel combo is king when asynchronously paging through a data source. If you’ll give me a few minutes of your time, I’d like to challenge that notion!

In this post, I’m going to show you how to implement great client-side paging, using jQuery and ASP.NET AJAX. I’ll be building on foundation laid in two previous posts: Use jQuery and ASP.NET AJAX to build a client side Repeater and How to easily enhance your existing tables with simple CSS. If you haven’t yet, I recommend that you read those posts first, so that we’re all on the same page.

This post will be a bit longer than those were, but I’ve tried to divide the ordeal into granular steps that break the monotony as much as possible:

  • Setting up a page method to retrieve individual pages of data.
  • Using jQuery to consume that page method and render its result.
  • Adding progress indication during the initial load.
  • Adding the basic HTML paging controls.
  • Wiring up event handlers for the paging controls.
  • Implementing the paging functions that those handlers call.
  • Adding progress indication during the paging operations.
  • Using a page method to dynamically determine the size of the data source.

Retrieving a particular “page” of data

Our existing GetFeedBurnerItems page method accepts a PageSize parameter, which specifies how many items it should return. To implement paging, we need to add a new parameter: Page.

This second argument will specify which page items will be returned from.

  1. [WebMethod]
  2. public static IEnumerable GetFeedburnerItems(int PageSize, int Page)
  3. {
  4.   XDocument feedXML =
  5.     XDocument.Load("http://feeds.encosia.com/Encosia");
  6.  
  7.   var feeds =
  8.     from feed in feedXML.Descendants("item")
  9.     select new
  10.     {
  11.       Date = DateTime.Parse(feed.Element("pubDate").Value)
  12.                      .ToShortDateString(),
  13.       Title = feed.Element("title").Value,
  14.       Link = feed.Element("link").Value,
  15.       Description = feed.Element("description").Value,
  16.     };
  17.  
  18.   return feeds.Skip((Page - 1) * PageSize).Take(PageSize);
  19. }

Using the supplied Page argument, our service uses IQueryable’s Skip() extension method to return the correct page of results.

The purpose of the subtraction is to make the Page argument 1-based. I don’t think the notion of a “page 0? makes much sense in this scenario, so I prefer they start at 1.

Consuming the service and rendering the result

Now that our service is modified to provide paged results, we can consume the first page of results similar to how we did in the previous examples.

  1. // Set this to any integer you like. 5-7 works well
  2. //  with the FeedBurner data source.
  3. var pageSize = 5;
  4.  
  5. $(document).ready(function() {
  6.   // Display the first page of results initially.
  7.   DisplayRSSTable(1);
  8. });
  9.  
  10. function DisplayRSSTable(page) {
  11.   $.ajax({
  12.     type: "POST",
  13.     url: "Default.aspx/GetFeedBurnerItems",
  14.     data: "{'PageSize':'" + pageSize + "', 'Page':'" + page + "'}",
  15.     contentType: "application/json; charset=utf-8",
  16.     dataType: "json",
  17.     success: function(msg) {
  18.       // Render the resulting data, via template.
  19.       ApplyTemplate(msg);
  20.     }
  21.   });
  22. }
  23.  
  24. function ApplyTemplate(msg) {
  25.   // Changed the template extension from .tpl to .htm,
  26.   //  to avoid the request being blocked by some IIS installs.
  27.   $('#Container').setTemplateURL('RSSTable.htm',
  28.                                  null, { filter_data: false });
  29.   $('#Container').processTemplate(msg);
  30. }

Since we’ll be calling the page method from other parts of the code, it made sense to refactor that functionality into a separate function: DisplayRSSTable.

Other than that, this is basically the same code that you’ve seen in the previous jQuery templating examples.

Adding initial progress indication on page load

FeedBurner is a great service, but tends to be a bit slow. Due to that latency on the response, the initial page often takes a few seconds to render. This has prompted several of you to ask about adding progress indication, which is a great idea.

There are many perfectly legitimate ways of accomplishing this, but I want to focus on minimizing the harshness of the transition between progress indication and content display. Jumping from a simple loading indicator to a full table is jarring.

One way to smooth that transition is to use a structure very similar to the final content’s template:

  1. <div id="Container">
  2.   <table id="RSSTable" cellspacing="0">
  3.     <thead>
  4.       <tr>
  5.         <th></th>
  6.       </tr>
  7.     </thead>
  8.     <tbody>
  9.       <td class="loading"></td>
  10.     </tbody>
  11.   </table>
  12. </div>

The .loading CSS class is defined as follows:

  1. table tbody .loading {
  2.   /* Since the table may be empty, set a decent default height. */
  3.   height: 350px;
  4.  
  5.   /* Center an animated progress indicator over the table body. */
  6.   background-image: url(images/progress-indicator.gif);
  7.   background-position: center center;
  8.   background-repeat: no-repeat;
  9. }

The end result is an empty table, with a centered progress indicator inside its single 350px tall body cell.

Adding paging controls to the HTML template

Now that the first page of results is coming through smoothly, we need to add a paging interface to get at the rest of the data. To keep this from running longer than a Steve Yegge post, let’s stick with a simple previous/next system.

Since it’s logically a part of the rendered template, I decided to add the paging controls to the template itself:

  1. <table id="RSSTable" cellspacing="0">
  2.   <thead>
  3.     <tr>
  4.       <th width="80">Date</th>
  5.       <th>Title / Excerpt</th>
  6.     </tr>
  7.   </thead> 
  8.   <tbody>
  9.     {#foreach $T.d as post}
  10.     <tr>
  11.       <td rowspan="2">{$T.post.Date}</td>
  12.       <td><a href="{$T.post.Link}">{$T.post.Title}</a></td>
  13.     </tr>
  14.     <tr>
  15.       <td>{$T.post.Description}</td>
  16.     </tr>
  17.     {#/for}
  18.   </tbody>
  19. </table>
  20.  
  21. <a id="PrevPage" class="paging">Previous Page</a>
  22. <a id="NextPage" class="paging">Next Page</a>

This template is very similar to the template that was used in the past two posts. The important changes are:

  • The post title and excerpt are rearranged to be vertically oriented.
  • The date column is fixed width, to maintain consistent column widths.
  • Of course, the paging controls are added to the bottom of the table.

After implementing these changes, our rendered table is shaping up nicely:

An example of the rendered template

Initializing the paging interface elements

Now that we’ve got the template rendering those anchor tags, the next step is to add functionality to make them do something:

  1. // Initialize this and store globally for tracking state.
  2. var currentPage = 1;
  3.  
  4. // The feed has 15 items, and we're displaying 5 per page.
  5. var lastPage = 3;
  6.  
  7. function UpdatePaging() {
  8.   // If we're not on the first page, enable the "Previous" link.
  9.   if (currentPage != 1) {
  10.     $('#PrevPage').attr('href', '#');
  11.     $('#PrevPage').click(PrevPage);
  12.   }
  13.  
  14.   // If we're not on the last page, enable the "Next" link.
  15.   if (currentPage != lastPage) {
  16.     $('#NextPage').attr('href', '#');
  17.     $('#NextPage').click(NextPage);
  18.   }
  19. }

UpdatePaging adds an href attribute of # to one or both of the paging anchor tags, thereby making them active links. This is primarily to give your users a good UI hint that they can click the links. The anchor’s actual navigation functionality won’t be needed.

.click() is jQuery’s way of attaching click handlers to elements. In this case, we’re wiring up a couple of functions that will handle the actual task of changing pages.

Putting it together, DisplayRSSTable needs to be updated to call UpdatePaging immediately after every template rendering:

  1. function DisplayRSSTable(page) {
  2.   $.ajax({
  3.     type: "POST",
  4.     url: "Default.aspx/GetFeedBurnerItems",
  5.     data: "{'PageSize':'" + pageSize + "', 'Page':'" + page + "'}",
  6.     contentType: "application/json; charset=utf-8",
  7.     dataType: "json",
  8.     success: function(msg) {
  9.       // Render the resulting data, via template.
  10.       ApplyTemplate(msg);
  11.  
  12.       // Wireup appropriate paging functionality.
  13.       UpdatePaging();
  14.     }
  15.   });
  16. }

By doing this, we can be sure that the paging functionality always reflects the currently rendered page.

Making it all work: implementing the paging functions

Finally, it’s time to implement the PrevPage and NextPage functions that are responsible for actually changing pages.

This is where you really appreciate that the navigation links are generated as part of the template. That allows us to fire off a DisplayRSSTable and not worry about updating the navigation click handlers. Those updates to the paging controls will be automatically handled by the UpdatePaging after the requested page’s template renders.

  1. function NextPage(evt) {
  2.   // Prevent the browser from navigating needlessly to #.
  3.   evt.preventDefault();
  4.  
  5.   // Entertain the user while the previous page is loaded.
  6.   DisplayProgressIndication();
  7.  
  8.   // Load and render the next page of results, and
  9.   //  increment the current page number.
  10.   DisplayRSSTable(++currentPage);
  11. }
  12.  
  13. function PrevPage(evt) {
  14.   // Prevent the browser from navigating needlessly to #.
  15.   evt.preventDefault();
  16.  
  17.   // Entertain the user while the previous page is loaded.
  18.   DisplayProgressIndication();
  19.  
  20.   // Load and render the previous page of results, and
  21.   //  decrement the current page number.
  22.   DisplayRSSTable(--currentPage);
  23. }

The call to preventDefault cancels the browser’s navigation to #. It doesn’t interfere with the paging functionality to allow that navigation, but allowing it will unnecessarily send mixed signals to your users.

Using the currentPage global variable, we can use our original DisplayRSSTable function to request one page up or down from the current page.

Progress indication during paging requests

Since each page request takes a couple seconds to complete, it’s important to provide progress indication during that time. This can be implemented in the same way as the initial page load progress indication. We’ll even reuse the same .loading CSS class.

  1. function DisplayProgressIndication() {
  2.   // Hide both of the paging controls,
  3.   //  to avoid click-happy users.
  4.   $('.paging').hide();
  5.  
  6.   // Clean up our event handlers, to avoid memory leaks.
  7.   $('.paging').unbind();
  8.  
  9.   // Store the height of the content area of the table.
  10.   var height = $('#RSSTable tbody').height();
  11.  
  12.   // Replace the entire content area with a single row/cell.
  13.   $('#RSSTable tbody').html('<tr><td colspan="2"></td></tr>');
  14.  
  15.   // Set that row's height to be the same as previous.
  16.   $('#RSSTable tbody tr').height(height);
  17.  
  18.   // Add our centered progress indicator animation to it.
  19.   $('#RSSTable tbody td').addClass('loading');
  20. }

This function does the following:

  • Hides the paging controls and clears any event handlers on them.
  • Determines the current height of the table’s content area.
  • Drops the entire content area and replaces it with a single cell.
  • Sets that cell’s containing row’s height to the previously determined value.
  • Add the .loading class to that cell, so that a progress animation appears centered over the same areas that the content used to occupy.

Dynamically determine the size of our data

So far, we’ve been working under the assumption that there will always be 15 elements in the data source and that it will never change at run time. While this might be a workable assumption for my RSS feed, it certainly would not be for most other data sources.

We need to dynamically detect the size of our data.

The first step in implementing this functionality is to interrogate our data source on the server side. This page method will return the total number of items in the feed:

  1. [WebMethod]
  2. public static int GetFeedBurnerItemCount()
  3. {
  4.   XDocument feedXML =
  5.     XDocument.Load("http://feeds.encosia.com/Encosia");
  6.  
  7.   return feedXML.Descendants("item").Count();
  8. }

Using the $.ajax() syntax that I have previously described, we can request the count of data items from our new page method. Using that and the global pageSize variable, it’s easy to determine the lastPage:

  1. // Initialize this to 1, so that "Next" is disabled until
  2. //  GetItemCount returns and we know there's a second page.
  3. var lastPage = 1;
  4.  
  5. function GetRSSItemCount() {
  6.   $.ajax({
  7.     type: "POST",
  8.     url: "Default.aspx/GetFeedBurnerItemCount",
  9.     data: "{}",
  10.     contentType: "application/json; charset=utf-8",
  11.     dataType: "json",
  12.     success: function(msg) {
  13.       // msg.d will contain the total number of items.
  14.       // Divide and round up to find total number of pages.
  15.       lastPage = Math.ceil(msg.d / pageSize);
  16.  
  17.       // Wireup appropriate paging functionality.
  18.       UpdatePaging();
  19.     }
  20.   });
  21. }

Now that we’re doing things more correctly, it makes more sense to initialize the lastPage variable at one. Until we’ve called our new GetFeedBurnerItemCount method and know the size of the data, we have no reason to assume there is more than one page of data.

To put this in action, we can add it to the $(document).ready handler:

  1. $(document).ready(function() {
  2.   // On page load, display the first page of results.
  3.   DisplayRSSTable(1);
  4.  
  5.   // Simultaneously, begin loading the total item count.
  6.   GetRSSItemCount();
  7. });

The end result is that both paging controls will originally be in a disabled state, until GetRSSItemCount is able to verify that at least (pageSize + 1) items exist in the data source.

If you’re implementing paging against a relatively static data source, like my RSS feed, there’s no need to add this functionality. However, I’m providing the added flexibility in hopes that it will make this more easily adaptable to your particular situation.

Conclusion

This has been a long post, but hopefully each individual part was digestible. As with most things, deconstructing the problem into manageable pieces makes the overall task easy. A few more thoughts include:

Error Handling. If you use this code, please do add error and timeout handling to your service calls. For the sake of brevity and clarity, I don’t address error handling in posts not specifically about that topic. However, you should never do that in production.

Caching. Depending on your data source, you may want to make use of the Cache class in your page method. For instance, this RSS example would benefit massively from Caching. However, you may just as likely be paging into a huge data source, in which case it wouldn’t make any sense to Cache.

Do keep it in mind though. With caching, paging through this example is almost instantaneous after the initial load.

That’s it. I hope you found this helpful.

Download the source

client-paging.zip (38kb)

###

Originally posted at Encosia.com. If you're reading this on another site, come on over and take a look at the real thing.

Easily build powerful client-side AJAX paging, using jQuery

How to easily enhance your existing tables with simple CSS

Posted on August 13, 2008 by Dave Ward.
Categories: ASP.NET, Contributors.

In two of my recent posts, I demonstrated using ASP.NET AJAX in conjunction with jQuery or ASP.NET AJAX 4.0’s templating features to generate client side tables from JSON data. However, what I neglected to show you was how to make them look even remotely presentable.

In this post, we’ll continue where those posts left off. I’m going to show you how you can improve the table’s presentation in three steps:

  • Setting basic styles for the page and table.
  • Improving the table header by using strong contrast.
  • Adding subtle gridlines to the table body.

A humble beginning and a basic foundation

At the end of both my previous posts on client-side templating, you were left with an HTML table that looked like this:

Though the table’s semantic structure is excellent, it is visually abysmal. Luckily, the table’s markup is very well suited to extensive CSS styling, so improving its presentation will be relatively painless.

Getting started, I’m going to apply styles to the container div and root table element, giving them a fixed width and centering them. I’ll also specify a better font than Times New Roman:

  1. /* No one likes Times New Roman, including me. */
  2. body { font-family: "Trebuchet MS", Serif; }
  3.  
  4. /* Gives the container a fixed
  5.     width and centers it on the page. */
  6. #Container { margin: 0 auto; width: 900px; }
  7.  
  8. /* Expand the table to fill the container, and
  9.     add a 2px black border around its outside edge. */
  10. table { width: 100%; border: 2px solid #000; }

Note that adding this border style to the table through CSS is not the same as setting the table element’s border attribute to 2. This CSS will add a border only to the outside of the table, whereas the table’s border attribute would add borders around and between every cell.

Use contrast to improve the <thead>

By default, most browsers will render a <thead>’s <th> elements in a bold and slightly larger font style. That’s a good start, but we can do better:

  1. table thead tr th {
  2.   /* Set the heading's background image to something nice.
  3.       Use a similar background color, to maintain continuity. */
  4.   background-color: #000;
  5.   background-image: url(images/black-gradient.png);
  6.  
  7.   /* Set the foreground color to something readable on black. */
  8.   color: #FFF;
  9.  
  10.   /* Give the heading a larger, bold style */
  11.   font-size: 18px;
  12.   font-weight: bold;
  13.  
  14.   /* Finally, make it the same height as the background image. */
  15.   height: 35px;
  16. }

This styling makes the <thead> area of the table look like this:

The dark background and reversed text both help to strongly define the heading row and draw your eye to the table’s intended starting point. Unless you have a captive audience, minimizing the time your users spend visually scanning the page is vitally important.

Since the background image covers the entire height of the <thead>, setting a background color may seem redundant, but is a good precaution. In case the image loads slowly, times out, or even 404s, your table is still usable.

Subtle improvements to the <tbody>

The net benefit of zebra striping and gridlines inside table bodies has been a point of contention lately. I’m not entirely sold on zebra striping myself, but believe that subtle gridlines between table cells are a great usability gain.

  1. table tbody td {
  2.   /* Pure black is harsh on a white background. Soften slightly. */
  3.   color: #333;
  4.  
  5.   /* Give the &amp;lt;td&amp;gt; cell content some breathing room. */
  6.   padding: 4px;
  7.  
  8.   /* Create the illusion of gridlines between the cells. */
  9.   border-top: 1px solid #CCC;
  10.   border-left: 1px solid #CCC;
  11. }

The top and left cell borders fit together to give the illusion of continuous gridlines, similar to what the GridView produces:

Using pseudo selectors to remove a minor artifact

One drawback of applying a top and left border to all of the table cells is that the cell borders overlap against the table’s borders on the top and left edge of the table body. In the color scheme I’ve chosen for this example, the artifact is barely visible, but that won’t be the case in every implementation.

We can use CSS’ :first-child pseudo class to remove those redundant borders:

  1. /* Remove the border from the top row. */
  2. table tbody tr:first-child td { border-top: none; }
  3.  
  4. /* Remove the border from the left column. */
  5. table tbody tr td:first-child { border-left: none; }

Compared to most CSS, the :first-child and :last-child pseudo selectors tend to be abnormally confusing. This rule of thumb may help you:

Element:first-child does not mean that the style will be applied to the element’s first child. Instead, :first-child means that the style will be applied if the element is the first child of its own parent element.

Note: These pseudo selectors do not work in IE6, but fail gracefully. If you’re still supporting a large number of IE6 users, you might want to investigate altogether different methods of implementing the gridlines (there are several).

Conclusion

As you’ve now seen, a small amount of CSS goes a long way toward making your tables presentable. I think it’s always preferable to style tables this way, but even more so when generating them on the client-side. The last thing you want to do is mix presentation with markup and complicate the templating process.

I had hoped to squeeze client-side sorting into this post, but ran out of space and time. So, I’ll be continuing this series at least one more post, showing you how to implement client-side sorting and paging for these templating examples.

I think once you see the series concluded, you’ll never again consider using a sluggish UpdatePanel and GridView in heavily trafficked areas of your apps. The performance difference is truly astounding to see firsthand.

As always, subscribe via RSS or email to make sure you don’t miss a post.

Get the source

The source download for this post also includes client-side sorting. Enjoy.

styled-client-repeater.zip (31kb)

Originally posted at Encosia.com. If you're reading this on another site, come on over and take a look at the real thing.

How to easily enhance your existing tables with simple CSS

A sneak peak at ASP.NET AJAX 4.0’s client-side templating

Posted on July 23, 2008 by Dave Ward.
Categories: ASP.NET, Contributors.

Hot on the heels of the recent ASP.NET AJAX roadmap, Bertrand and team have released a limited preview of the new AJAX functionality coming in ASP.NET 4.0.

To see how the new functionality stacks up, I decided to recreate my recent jTemplates example, using only ASP.NET AJAX and its new templating features. Eventually, I settled on using the DataView class, which offers more advanced, repeater-like functionality.

Having successfully completed the exercise, I thought it seemed like something that you might find interesting too. The solution boils down to four easy steps:

  • Creating a page method to return JSON data.
  • Setting up a ScriptManager to coordinate script and page method access.
  • Defining the client-side template that will render the JSON data.
  • Using JavaScript to render the template, using the page method’s return.

A familiar page method

The first order of business is obtaining a data source to render. To keep things simple, let’s reuse the RSS reader from my jQuery “repeater” example:

  1. [WebMethod]
  2. public static IEnumerable GetFeedburnerItems()
  3. {
  4.   XDocument feedXML =
  5.     XDocument.Load(&quot;http://feeds.encosia.com/Encosia&quot;);
  6.  
  7.   var feeds =
  8.     from feed in feedXML.Descendants(&quot;item&quot;)
  9.     select new
  10.     {
  11.       Date = DateTime.Parse(feed.Element(&quot;pubDate&quot;).Value)
  12.                      .ToShortDateString(),
  13.       Title = feed.Element(&quot;title&quot;).Value,
  14.       Link = feed.Element(&quot;link&quot;).Value,
  15.       Description = feed.Element(&quot;description&quot;).Value
  16.     };
  17.  
  18.   return feeds;
  19. }

This page method will return a JSON array representing the latest ten items from my RSS feed. Specifically, their Date, Title, Link, and Description.

Setting up the ScriptManager

The ASP.NET AJAX 4.0 templating features depend on functionality in the ASP.NET AJAX client side framework. We could include MicrosoftAjax.js manually, but since we’re also using a page method, the ScriptManager will serve our needs well:

  1. &lt;asp:ScriptManager runat=&quot;server&quot; EnablePageMethods=&quot;true&quot;&gt;
  2.   &lt;Scripts&gt;
  3.     &lt;asp:ScriptReference Path=&quot;~/MicrosoftAjaxTemplates.js&quot; /&gt;
  4.     &lt;asp:ScriptReference Path=&quot;~/default.js&quot; /&gt;
  5.   &lt;/Scripts&gt;
  6. &lt;/asp:ScriptManager&gt;

EnablePageMethods will generate a JavaScript proxy for calling our page method. This is not necessarily the only or most efficient way of calling page methods, but works well enough for our purposes here.

Creating the template

Next, we need to define a template to render the page method’s return upon. The ASP.NET AJAX 4.0 templating engine’s syntax is very straightforward:

  1. &lt;table&gt;
  2.   &lt;thead&gt;
  3.     &lt;tr&gt;
  4.       &lt;th&gt;Date&lt;/th&gt;
  5.       &lt;th&gt;Title&lt;/th&gt;
  6.       &lt;th&gt;Excerpt&lt;/th&gt;
  7.     &lt;/tr&gt;
  8.   &lt;/thead&gt;
  9.   &lt;tbody id=&quot;RSSItem&quot; class=&quot;sys-template&quot;&gt;
  10.     &lt;tr&gt;
  11.       &lt;td&gt;{{ Date }}&lt;/td&gt;
  12.       &lt;td&gt;&lt;a href=&quot;{{ Link }}&quot;&gt;{{ Title }}&lt;/a&gt;&lt;/td&gt;
  13.       &lt;td&gt;{{ Description }}&lt;/td&gt;
  14.     &lt;/tr&gt;
  15.   &lt;/tbody&gt;
  16. &lt;/table&gt;

The bracketed field names correspond to the names of the properties in the anonymous type generated by the page method. It couldn’t be much easier.

You may have noticed the sys-template CSS class applied to the meat of the template. This is a convention for hiding the template until it has been rendered. The class should be defined somewhere as:

  1. .sys-template { display: none; visibility: hidden; }

During the rendering process, the templating engine will automatically attempt to remove the sys-template class, making the final result visible.

Bringing it all together

Now that we’ve got our JSON data source and have defined our template, the final step is simply to connect those dots.

  1. Sys.Application.add_init(AppInit);
  2.  
  3. // Execute the page method when the page finishes loading.
  4. function AppInit(sender, args) {
  5.   PageMethods.GetFeedburnerItems(OnSuccess, OnFailure);
  6. }
  7.  
  8. function OnSuccess(result) {
  9.   // Create an ASP.NET AJAX 4.0 DataView, targeted at our template.
  10.   var dv = new Sys.Preview.UI.DataView($get('RSSItem'));
  11.  
  12.   // Pass the DataView our JSON result of RSS items to render.
  13.   dv.set_data(result);
  14.  
  15.   // Render the DataView template, using the provided JSON array.
  16.   dv.render();
  17. }
  18.  
  19. // Do not do this in production code!
  20. function OnFailure() { }

The OnSuccess function is where all of the magic happens. When the page method completes its execution, three things happen:

  • A DataView object is created for our template, by passing its containing DomElement: $get(’RSSItem’).
  • The JSON result is assigned to the DataView, using its set_data method.
  • The DataView’s render method is called to generate the end result.

Conclusion and a couple suggestions.

Comparing this implementation to my identical jTemplates example, I must say that I prefer the DataView solution. The syntax is impressively cleaner.

I just want to reiterate how happy I am with the transparency of the ASP.NET team lately. For those of us who aren’t MVPs or ASPInsiders, it’s nice to have a chance to offer constructive feedback and generally not be left in the dark.

In that spirit, I have two suggestions to improve the templating engine.

External templates. I don’t particularly enjoy mingling my template with the rest of the page’s HTML. This is one thing that jTemplates handles very well, with its createTemplateURL method. I would love to see this functionality in the ASP.NET AJAX templating engine and/or the DataView class.

To one-up jTemplates, it would be fantastic if there were a way to pre-load the template. During the second or two that the AJAX call executes would be an excellent time to get the template ready, so that there’s no HTTP delay after the data is ready.

Table issues. Ideally, there should be an automatic convention for hiding the entire table until the template is rendered. Maybe that’s already possible, but I wasn’t able to find a configuration that would do that while repeating only the tbody.

It would make sense if we could assign the entire table a CSS class of sys-template, which would be removed if any of its children were a template being rendered. I usually avoid special cases like this, but I expect that the table scenario will be a prevalent one. It makes sense to optimize for it.

Try it for yourself

dataview-demo.zip (12kb)

a

A sneak peak at ASP.NET AJAX 4.0’s client-side templating

The Future of ASP.NET AJAX

Posted on July 15, 2008 by Dave Ward.
Categories: ASP.NET, Contributors.

Bertrand LeRoy recently published a 13 page document on CodePlex, describing the ASP.NET Team’s goals for the future of ASP.NET AJAX. It’s been available for two weeks now, but there has been little reaction to it online and less than 8,000 people have downloaded it. I’m surprised, considering its importance.

If you haven’t yet read it, I encourage you to download and read it right now.

Now that you’ve read the official document, continue reading here for my take on what I think are the most significant items in the document. What follows is broken down into sections matching the road map document:

Persistent client-side event sinks

The following example shows setting up an event sink that will result in the title attribute of all spans getting dumped to the debug trace. This starts working at the moment the call is made and will continue working even for new spans that get created after that.

  1. $listen(“hover”, “span[title]”, function(e) {
  2.   Sys.Debug.trace(this.title);
  3. });

The average web page’s DOM is becoming increasingly volatile. Elements may be destroyed, appended, and replaced many times between each page refresh. Maintaining event handlers on those relatively ephemeral elements is often a significant headache.

Hence I think $listen has great potential, with one caveat: Performance.

If I have a dozen $listen sinks set up, what happens when I dump a thousand new elements into a table or select list? Will a $query be executed for each $listen, on every DOM modification?

If $listen introduces client-side hangs during those sorts of operations, the convenience would not justify the trade-off. Please be sure to include that sort of scenario in your testing.

Client Data and UI Templates: The Good

For performance critical functionality, using something like the UpdatePanel will never be a panacea. It was a noble effort, but trades far too much performance for its simplicity. When speed counts, the only thing on the wire should be data.

For that reason, I’m very happy to see client-centric markup generation being officially supported. Check out this example code, from the road map document:

  1. &lt;div id=&quot;repeater1&quot;&gt;&lt;/div&gt;
  2.  
  3. &lt;div id=&quot;template1&quot; class=&quot;sys-template&quot;&gt;
  4.   &lt;h2&gt;&lt;a href=&quot;{{ &#39;products/&#39; + id }}&quot;&gt;{{name}}&lt;/a&gt;&lt;/h2&gt;
  5.   &lt;p&gt;{{description}}&lt;/p&gt;
  6. &lt;/div&gt;
  7.  
  8. &lt;script type=&quot;text/javascript&quot;&gt;
  9.   Sys.Application.add_initialize(function() {
  10.   $create(Sys.UI.DataView, {
  11.     template: $get(&quot;template1&quot;),
  12.     data: myData
  13.   }, {}, {}, $get(&quot;repeater1&quot;));
  14. }
  15. &lt;/script&gt;

This implementation is fundamentally similar to my recent post on creating a client-side repeater with jQuery and ASP.NET AJAX. I think it will be a solid step in the right direction, and it will be great to see the technique become more mainstream in v.Next.

Client Data and UI Templates: The Ugly

The road map goes on to describe more complex templating, attaching client-side behaviors through xmlns attributes, and even a client-side analogue for DataSource server controls. Here’s an example:

  1. &lt;body xmlns:sys=&quot;javascript:Sys&quot;
  2.       xmlns:dv=&quot;javascript:Sys.UI.DataView&quot;&gt;
  3.  
  4.  
  5. &lt;div id=&quot;tripList&quot; sys:attach=&quot;dv&quot; dv:data=&quot;{{myData}}&quot; 
  6.     dv:template=&quot;{{$get(&quot;template2&quot;)}}&quot;&gt;&lt;/div&gt;
  7.  
  8. &lt;div id=&quot;template2&quot; class=&quot;sys-template&quot;
  9.   xmlns:ac=&quot;javascript:Sys.UI.AutoComplete&quot;
  10.   xmlns:wm=&quot;javascript:Sys.UI.Watermark&quot;
  11.   xmlns:dp=&quot;javascript:Sys.UI.DatePicker&quot;&gt;
  12.  
  13.   &lt;input type=&quot;text&quot; sys:id=&quot;{{ &quot;airport&quot; + $index }}&quot;
  14.     sys:attach=&quot;ac,wm&quot;
  15.     ac:serviceUrl=&quot;airportList.asmx&quot;
  16.     ac:minimumPrefixLength=&quot;{{1}}&quot;
  17.     wm:text=&quot;Type the name of an airport&quot;
  18.     value=&quot;{Binding airport, mode=twoWay}&quot; /&gt;
  19.   &lt;input type=&quot;text&quot; sys:id=&quot;{{ &quot;flight&quot; + $index }}&quot;
  20.     value=&quot;{Binding flight, mode=twoWay}&quot; /&gt;
  21.   &lt;input type=&quot;text&quot; sys:id=&quot;{{ &quot;date&quot; + $index }}&quot;
  22.     sys:attach=&quot;dp&quot;
  23.     dp:lowerBound=&quot;{{ new Date(1970, 4, 21) }}&quot;
  24.     dp:upperBound=&quot;{{ new Date(2050, 1, 1) }}&quot;
  25.     value=&quot;{Binding date, mode=twoWay}&quot; /&gt;
  26. &lt;/div&gt;

In my experience, this code is not maintainable. Unless spectacular Intellisense and designer support accompanies its release, I wouldn’t expect it to see much use in production.

Further, the behavior and content should be separated; especially given that one of the road map’s stated goals is accessibility. Unobtrusive JavaScript is the way to go here, not overloaded declarative markup.

Can we get this functionality in a way that is conducive to separation of concerns?

Improved animation support

I bet there are more people who have used the toolkit’s AnimationExtender once than there are who have used it twice. It’s a very powerful animation framework, but unwieldy and difficult to learn.

For that reason, it’s a relief to see the XML syntax being abandoned for fluent JavaScript objects. The example from the road map:

  1. $query(&quot;.sprite&quot;).animate([
  2.     new Sys.Animation.FadeIn(300),
  3.     {
  4.       &quot;style.backgroundColor&quot;: &quot;#ff0000&quot;,
  5.       &quot;style.fontSize&quot;: &quot;2em&quot;),
  6.       duration: 500
  7.     },
  8.     new Sys.Animation.FadeOut(300)
  9.   ])
  10. );

That’s a massive improvement over the AnimationExtender, but still a bit verbose. It would be great if that could be further distilled down to something like this:

  1. $query(&quot;.sprite&quot;).FadeIn(300)
  2.                  .FadeCSS({
  3.                    &quot;backgroundColor&quot;:&quot;#f00&quot;,
  4.                    &quot;fontSize&quot;:&quot;2em&quot;,
  5.                    duration: 500 })
  6.                  .FadeOut(300);

New AJAX Control Toolkit components

The next section in the road map describes several new AJAX Control Toolkit controls in the works, and solicits community input:

Which controls get implemented first will highly depend on community feedback. We welcome suggestions and encourage you to communicate your preferences about which controls would be the most useful to you as well as what features you would expect on each of them. We identified the following controls as possible candidates:

High priority (v.Next):

  • Grid. Make this work with ASMX and WCF JSON serialized services, and it will be the most popular control in the toolkit. Be sure that it renders <thead> and <tbody> elements, unlike the GridView.
  • Upload. We need a solid solution to the async upload problem. It’s not something that would be used as often as the Grid, but would be nearly irreplaceable.
  • Asynchronous validator. I began writing an asynchronous username availability validator last year, but became disillusioned before overcoming all of the hurdles involved. I think an asynchronous validator base control that allows custom, server-side validation rules would be a great addition.

Would be nice later:

  • Progress bar. How would this work? If it were easy to use, it could be very popular. It shouldn’t be rushed though. Save this till later and take the time to perfect it.
  • TreeView. This isn’t something I would use very much personally, but I think it would be welcomed by many developers.

Low priority:

  • Layout. How many real websites use these ubiquitous splitter controls? They are typically indicative of a poor web UI, attempting to copy the layout of a thick client. We can do without this.
  • Chart. It makes no sense to reinvent this wheel. A Silverlight based control isn’t going to be very widely adopted right now. More importantly, it doesn’t make sense to alienate the companies who have been supplying excellent visualization controls for years.
  • Color picker and rich text editor. As with charting, this functionality has been available for many years. There are mature solutions already available. Your efforts would be needlessly squandered here.

JavaScript build tools

Aggregation and minification (is that a word?) is a constant thorn in my side. More details on implementation specifics would be nice, but I think the feature can only be an improvement.

While you’re supporting minify, you might as well add packer support as well. Depending on the speed of the client machine and execution order of scripts, packed scripts may load faster than minified ones, and can also be used to provide minimal obfuscation.

Ideally, this would enable us to work with an uncompressed, commented JavaScript include in debug mode. Then, in release builds, the include would be optimized and combined with other scripts. Please keep in mind that we may be using scripts hosted externally, which also have separate debug and release versions. A mechanism to allow for that would be excellent.

I think that the ability to add script references at the web.config level could be very useful as well. For example, I may want to reference jQuery throughout my entire website. Save me the trouble of adding that script include on every page, and I’ll buy you a beer the next time you’re in Atlanta.

Stay focused on your core users

Finally, I was surprised to see this ambitious goal early in the document:

Make ASP.NET Ajax the first-class choice for all Web 2.0 developers

I don’t mean to sound negative, but that is simply attempting to bite off more than even Microsoft can chew. At least in the near-term, developers using other application stacks or JavaScript frameworks aren’t going to replace what they’re using with v.Next of the client side ASP.NET AJAX library.

We need the ASP.NET Team to focus on the needs of those of us using ASP.NET AJAX today. Empower existing ASP.NET developers to create robust, dynamic websites, and you’ll find that our work has the potential to reach broad audiences on your behalf.

A few additional thoughts

Stop trying to hide JavaScript from us. Similar to the UpdatePanel, things like Script# and Volta are neat, but they are leaky abstractions. I hope they don’t significantly influence the ASP.NET Team’s vision of the future of AJAX.

It’s like Joe said: JavaScript is inevitable. Why learn to use Volta or Script#, when you could just learn JavaScript and be done with it?

I don’t want to become a client-side architecture astronaut.

Refreshing UpdatePanels via JavaScript. A year later, my post about using __doPostBack to refresh an UpdatePanel is still the most trafficked page on this site. v.Next ought to have a better way of doing this, as it’s obviously a common need.

I believe $query(”UpdatePanel1?).Update() would be very well received.

Host the client library for us. Steven Smith mentioned this back in April. Yahoo hosts all of the YUI scripts on their CDN. Google now hosts jQuery, prototype, script.aculo.us, MooTools, and dojo. There’s no excuse for Microsoft not to follow suit here.

For those of us writing public facing ASP.NET applications, this can be a non-trivial factor in deciding which JavaScript framework to use. In addition to a Google or Yahoo CDN being more performant than most any hosting setup, your users may even show up with the library already cached.

Conclusion

It’s no secret that myself and several others have been drifting toward jQuery this year. I sincerely hope that ASP.NET AJAX v.Next might begin to reverse that trend.

The ASP.NET Team appears to have some exciting features in store for us. I hope that we start seeing “futures” releases soon. Maybe some idea of a tentative release timeline?

What do you think? Did I miss anything important?

Originally posted at: Encosia.

Use jQuery and ASP.NET AJAX to build a client side Repeater

Posted on June 26, 2008 by Dave Ward.
Categories: ASP.NET, Contributors.

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.

  1. [WebMethod]
  2. public static IEnumerable GetFeedburnerItems(int Count)
  3. {
  4.   XDocument feedXML =
  5.     XDocument.Load("http://feeds.encosia.com/Encosia");
  6.  
  7.   var feeds =
  8.     from feed in feedXML.Descendants("item")
  9.     select new
  10.     {
  11.       Date = DateTime.Parse(feed.Element("pubDate").Value)
  12.                      .ToShortDateString(),
  13.       Title = feed.Element("title").Value,
  14.       Link = feed.Element("link").Value,
  15.       Description = feed.Element("description").Value
  16.     };
  17.  
  18.   return feeds.Take(Count);
  19. }

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:

  1. $(document).ready(function() {
  2.   $.ajax({
  3.     type: "POST",
  4.     url: "Default.aspx/GetFeedburnerItems",
  5.     // Pass the "Count" parameter, via JSON object.
  6.     data: "{'Count':'7'}",
  7.     contentType: "application/json; charset=utf-8",
  8.     dataType: "json",
  9.     success: function(msg) {
  10.       BuildTable(msg.d);
  11.     }
  12.   });
  13. });

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:

  1. [{"Date":"6/5/2008",
  2.   "Title":"3 mistakes to avoid when using jQuery with ASP.NET AJAX",
  3.   "Link":"http://encosia.com/2008/06/05/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/",
  4.   "Description":"Three common problems that I've seen when using jQuery with ASP.NET AJAX, their underlying causes, and simple solutions to them."},
  5.  
  6.  {"Date":"5/29/2008",
  7.   "Title":"Using jQuery to directly call ASP.NET AJAX page methods",
  8.   "Link":"http://encosia.com/2008/05/29/using-jquery-to-directly-call-aspnet-ajax-page-methods/",
  9.   "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:

  1. function BuildTable(msg) {
  2.   var table = '<table><thead><tr><th>Date</th><th>Title</th><th>Excerpt</th></thead><tbody>';
  3.  
  4.   for (var post in msg)
  5.   {
  6.     var row = '<tr>';
  7.  
  8.     row += '<td>' + msg[post].Date + '</td>';
  9.     row += '<td><a href="' + msg[post].Link + '">' + msg[post].Title + '</a></td>';
  10.     row += '<td>' + msg[post].Description + '</td>';
  11.  
  12.     row += '</tr>';
  13.  
  14.     table += row;
  15.   }
  16.  
  17.   table += '</tbody></table>';
  18.  
  19.   $('#Container').html(table);
  20. }

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:

  1. <table>
  2.   <thead>
  3.     <tr>
  4.       <th>Date</th>
  5.       <th>Title</th>
  6.       <th>Excerpt</th>
  7.     </tr>
  8.   </thead>
  9.   <tbody>
  10.     {#foreach $T.d as post}
  11.     <tr>
  12.       <td>{$T.post.Date}</td>
  13.       <td><a href="{$T.post.Link}">{$T.post.Title}</a></td>
  14.       <td>{$T.post.Description}</td>
  15.     </tr>
  16.     {#/for}
  17.   </tbody>
  18. </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.tpl.

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.

  1. function ApplyTemplate(msg) {
  2.   // This method loads the HTML template and
  3.