.NET Unplugged

July 1, 2009

ASP.NET MVC Grid – Part 3 – Sorting

Filed under: ASP.NET MVC — Eric P @ 11:57 am

For this part I implemented server side sorting.

screenshot

click image to see full screenshot

You can see demo here:
http://samples.entechsolutions.com/MvcGridSample/Part3

Code is available here:
http://samples.entechsolutions.com/Downloads/MvcGridSample/MvcGridSample_Part3.zip

Implementation

1. Started by adding sort links for ID column

	<th>
	     ID
	     <a href="#" onclick="sort('id', 'asc');">asc</a>
	     <a href="#" onclick="sort('id', 'desc');">desc</a>
	</th>

2. Added javascript and hidden fields to pass sorting information to controller

	<script type="text/javascript">
 	 	...

		function sort(sortField, sortDirection) {
			$("#gridAction").val("Sorted");
			
			$("#sortField").val(sortField);
			$("#sortDirection").val(sortDirection);

			submitForm();
		}    
				
		function submitForm() {
			var form = $("#grid").parents("form:first");
			form.submit();
		}            
	</script> 
 	
...

<%= Html.Hidden("sortField") %>
<%= Html.Hidden("sortDirection") %>

3. Modified CustomerController to handle sorting

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult List(int currentPage, int pageSize, string sortField, string sortDirection, string gridAction)
{
	if (gridAction == "PageSizeChanged" || gridAction == "Sorted")  //Resetting currentPage on Sorted action
		currentPage = 1;

	IQueryable<Customer> query = _customerService.GetQueryable();
	int totalRows = query.Count();
	if (totalRows==0)
		return View(new List<Customer>());

	//Update query with order by clause
	if (!String.IsNullOrEmpty(sortField))
		query = AddQuerySorting(query, sortField, sortDirection);

	int totalPages = (int)Math.Ceiling((double)totalRows / (double)pageSize);
	if (totalPages != 1)
		query = AddQueryPaging(query, pageSize, currentPage);
    
	//Update ViewData with new sort params
	UpdateSorterViewData(sortField, sortDirection);
	UpdatePagerViewData(totalPages, totalRows, currentPage, pageSize);
	
	List<Customer> customers = query.ToList();
	return View(customers);
}


private IQueryable<Customer> AddQuerySorting(IQueryable<Customer> query, string sortField, string sortDirection)
{
	//Used approach from http://www.singingeels.com/Articles/Self_Sorting_GridView_with_LINQ_Expression_Trees.aspx
	//instead of a long switch statement 
	var param = Expression.Parameter(typeof(Customer), "customer");
	var sortExpression = Expression.Lambda<Func<Customer, object>>
							(Expression.Convert(Expression.Property(param, sortField), typeof(object)), param);

	if (sortDirection == "Asc")
		query = query.OrderBy(sortExpression);
	else
		query = query.OrderByDescending(sortExpression);

	return query;
}


private void UpdateSorterViewData(string sortField, string sortDirection)
{
	ViewData["sortField"] = sortField;
	ViewData["sortDirection"] = sortDirection;
}

4. Changed UI to only display asc/desc image next to column being sorted. Also changed column heading labels to links. Used the following CSS to display ASC or DESC image next to link that was sorted.

#grid #data .asc{
	background: transparent url('../Content/Images/asc.png') center right no-repeat ;
}

#grid #data .desc{
	background: transparent url('../Content/Images/desc.png') center right no-repeat;
}

5. Made all columns sortable. Moved some of the sorting display logic to controller to prevent too much duplication.

Html was simplified to look like this:

<th>
	<a href="#" class="<%= CustomerController.GetGridThClass(ViewData, "FirstName") %>" 
				onclick="<%= CustomerController.GetGridThOnClick(ViewData, "FirstName")%>">
		First Name</a>
</th>

The new methods in CustomerController are:

public static string GetGridThClass(ViewDataDictionary<IEnumerable<Customer>> viewData, string sortField)
{
	if ((string)viewData["SortField"] != sortField)
		return "";

	if ((string)viewData["SortDirection"] == "Asc")
		return "asc";
	
	return "desc";
}


public static string GetGridThOnClick(ViewDataDictionary<IEnumerable<Customer>> viewData, string sortField)
{
	if ((string)viewData["SortField"] == sortField && (string)viewData["SortDirection"] == "Asc")
		return "sort('" + sortField + "', 'Desc');";

	return "sort('" + sortField + "', 'Asc');";
}

Observations

  • Discovered a nice way to post code in WordPress using ‘sourcecode’ tag here: http://support.wordpress.com/code/
  • ViewData is starting to remind me of a ViewState. With all the hidden variables it is basically the same thing. Of course without being bloated and encoded and not usable from JS.

Coming up…

At this point we have a grid with server side paging and server side sorting.

For next part I will create a Grid class/data structure with all the Pager and Sorter properties and pass it as a View model instead of using the non-strong typed ViewData[...].
I will also add keyword search with auto-complete.

About these ads

6 Comments »

  1. There are some fundamental flaws with your approach that probably won’t become an issue if you’re building an intranet/internal application only. But I would consider them serious issues for a publish facing website.

    First the sorting/paging relies on JavaScript, what happens when JavaScript is switched off? Second the sorting & paging perform a POST, so that if the user tries to refresh the page they receive the annoying “You are about resend some form data” message. Third, it breaks the back button so that if I set records per page from 5 to 10, then click back, it still remains at 10. Finally, the biggest issue is that paging and sorting, and the general state of the grid, aren’t persisted to the URL querystring so that your grid can’t be bookmarked with the page and sorting (and any other filters) left intact, this also goes for sending the URL to friends via IM, forums, email etc. Say your friend is looking for a cheap hard drive, you’d go to an e-commerce site, go to the hard drives section, then order them by price ascending and maybe set paging to 20 records per page, then send the URL to your friend “Hey check out these cheap hard drives”. Normally the state of the grid (including paging/sorting info) would be stored in the querystring (eg. http://www.overclockers.co.uk/productlist.php?&groupid=701&catid=14&subid=940&sortby=priceAsc).

    Comment by sironfoot — July 3, 2009 @ 5:48 pm | Reply

    • Thank you for pointing out the flows. The grid is still work in progress and I welcome all the feedback that can help me to improve it.

      1. It is true that grid will not work if JS is turned off.

      To fix it, for paging/sorting links I could pass all the parameters in link’s query string (like in overclockers example you provided), instead of setting them using Javascript. For Page Size drop down to keep working – there could be a refresh button which would submit the form with all other parameters passed as hidden variables. I can display “refresh” button only if Javascript is disabled.

      There seems to be a heated discussions going on about supporting “Javascript disabled” users. For ex. here:
      http://alternateidea.com/blog/articles/2005/10/13/dispelling-the-myths-of-javascript-degradation.

      I thought the number of users with JS disabled was negligible, but it looks like around 5% (http://www.w3schools.com/browsers/browsers_stats.asp). This 5 % could be a pretty big segment for a large site.

      2. Persisting of the grid when user clicks back button is something I am currently working on.

      3. Using Query string (for bookmarking) instead of Post should be as simple as switching form method from “post” to “get”.

      Comment by Eric P — July 3, 2009 @ 10:26 pm | Reply

      • Thanks for the reply, good to hear that you’re working on those issues. I would agree that in the majority of instances lack of JavaScript isn’t really a big issue, I would guess the majority of people with JavaScript switched off probably know to switch it back on if they want to use your site.

        I would like to raise another point regarding the JavaScript only paging links though, how would a search engine follow those links so that it could follow and index additional products on page 2,3,4… etc.

        Comment by sironfoot — July 4, 2009 @ 12:30 pm

  2. [...] grid work if Javascript is disabled. This was brought to my attention by a comment in Part 3. Thank you, [...]

    Pingback by ASP.NET MVC Grid – Part 5 – Persisting Grid State « RAD for N-Tier web apps in .NET — July 10, 2009 @ 8:15 am | Reply

  3. ASP.NET MVC Grid – Part 3 – Sorting « RAD for N-Tier web apps in .NET…

    Thank you for submitting this cool story – Trackback from YOUR-TITLE…

    Trackback by YOUR-TITLE — April 3, 2010 @ 4:03 pm | Reply

  4. Hi, did You perhaps follow up on this “tutorial” by making the class you were talking about?

    Comment by aslezak123 — April 30, 2011 @ 1:29 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: