.NET Performance Blog

July 10, 2009

ASP.NET MVC Grid – Part 5 – Persisting Grid State

Filed under: ASP.NET MVC,General — Eric P @ 8:14 am

In this part I will implement the following items:

  • persist the state of the grid
  • update UI with row striping and row click actions
  • handle a “last customer on page” edge case
  • add status/notifications line when customer is added/deleted/modified
  • validate edit form
Click on the image to view the whole screenshot.

Click on the image to view the whole screenshot.

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

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

Implementation

1. Persist state of the grid
A common use case for any grid control – is remembering the state of the grid when user comes back to the list page. So if grid is currently on page 2 and sorted by phone and user clicks on Edit – then saves customer and goes back to grid – the grid will still be on page 2 and sorted by phone.

To implement this functionality I created a new service class GridStateService with methods
Save(key, Grid)
Grid Load(key)
bool Exists(key);

This service uses session to save/load grid’s state, so while the person is using the site and his/her session hasn’t expired, the grid state (current page, page size, etc…) persists. If needed, it should be simple to change the persistence mechanism to store grid state in the cookie or the database. In those cases, whenever user would come back to the site, his/her last preferences (like sorting by “Phone” or page-size=50) would remain.

Here is how GridStateService is used in CustomerController.List:


[AcceptVerbs(HttpVerbs.Get)]
public ActionResult List()
{
	string key = GetGridKey();   //This function returns current url used to uniquely identify the grid
	if (_gridStateService.Exists(key))
	{
		_grid = _gridStateService.Load(key);
	}
	else
	{
		_grid = new Grid<Customer>
					(
						new Pager {CurrentPage = 1, PageSize = 5}, 
						new Sorter("ID", SortDirection.Asc)
					);
	}

	UpdateGridData();

	return View(_grid);
}

Here is the code the saves last state of the grid.


[AcceptVerbs(HttpVerbs.Post)]
public ActionResult List(Grid<Customer> grid)
{
	_grid = grid;

	_grid.ProcessAction();

	UpdateGridData();

        //Check if there is data in the grid
	if (_grid.Data.Count() > 0)
		SaveGridState();
	
	return View(_grid);
}

private void SaveGridState()
{
	string key = GetGridKey();
	_gridStateService.Save(key, _grid);
}

2.Row striping and row click

The row striping and highlighting (which allows for better readability) was implemented using approach described in the following article:
http://webdevdotnet.blogspot.com/2009/06/aspnet-mvc-jquery-part-2-zebra-striping.html

As for row-clicking – I added onlcick handler to data row’s tr tag:

<tr onclick="onRowClick(<%= item.ID %>)">
     <td>

When user clicks on the row they will be taken to customer edit screen. This is handled by JS function:

function onRowClick(id) {
	document.location = "/Customer/Edit/" + id;
}

3. Edge case for deleting last customer on the page

If I am using 5 rows per page with 6 customers and I deleted sixth customer on the second page, the grid should automatically switch to page 1 instead of showing page 2 with no customers.

This condition was handled in pager using the following code:

//handle a case when user deleted all rows on last page
if (_currentPage > _totalPages)
    _currentPage = _totalPages;

4. Status line when customer is added/deleted/modified

This functionality is related to operations triggered in the grid page.
Previously, if I deleted a customer there would be no indication that customer was deleted. The grid would get refreshed, but there would be no explicit notification that customer was actually deleted.

I used TempData functionality on the MasterPage to display such messages/notifications. All operations, add/edit/delete, that are successfully performed on the customer are now followed by a status line message (yellow box) on the grid page.
This message fades out after 5 seconds (ain’t JQuery great).

5. Validate add/edit form
This functionality is not really grid-related, but I thought it would be important to have it for sample completeness. It is not very realistic to add a customer with all fields empty. It will also help when I implement inline editing.

I used Data Annonations approach described here:
http://www.asp.net/learn/mvc/tutorial-39-cs.aspx

I update UI a little to display error message right next to the input fields which are invalid.

Observations

  • Data annotations has a property DataType which takes DataType enumeation consisting of members like “Currency”, “Email Address”, etc… I originally thought it would validate my fields according to the type I pass, but that doesn’t work. But it doesn’t work this way. In one of the comments to article http://www.aspworkshops.com/blog/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx it states:

    The DataType attribute are not validators.
    So DataType(DataType.Email) will not validate a string for being an email address. These are ui type hint sttributes.

    GRRRRRRReat… I guess I will need a reg expression validator or custom validator for Email/Phone validations.

  • When messing with Data Annotations, I discovered another error. Even though it worked ok when binding simple models, I started getting NullReferenceException when binding to complex models (with sub objects). I discovered the fix here:

    http://stackoverflow.com/questions/820468/how-does-dataannotationsmodelbinder-work-with-custom-viewmodels

  • One more thing related to validation. Looks like there is no way to provide custom error messages for data type conversions during binding. So if user enters some text into “Orders Placed” field – the error message is always:

    The value ‘SOME VALUE’ is not valid for the Orders Placed field.

    This is confirmed by Scott Gu in one of the comments to the following article:
    http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx

    # re: ASP.NET MVC Preview 5 and Form Posting Scenarios
    Wednesday, September 03, 2008 11:39 PM by ScottGu

    Hi Florian,

    >>>>>>> I very much like the approach, cannot wait until this stuff is baked. One question – is there a way to intercept input validation and specify custom ErrorMessages?

    >>>>>>> E.g. in your example where the user enters a random string instead of the decimal number required by the model field you’re auto-generating an error message (‘.. invalid value ..’).

    >>>>>>> Is there a way to customize (and localize) this message, without inspecting and changing ModelState explicitely?

    Unfortunately with Preview 5 you need to manipulate the ModelState dictionary directly to enable this (either that or override the ModelBinder behavior to customize your own message).

    Hope this helps,

    Scott

    NOTE TO SELF: Add custom binder with a way to override type conversion error message.

Coming up…

There are a couple of items left before I will make the grid generic and re-usable. For the next part I will work on:

  • Making grid work if Javascript is disabled. This was brought to my attention by a comment in Part 3. Thank you, sironfoot.
  • Advandced search form – which will include fields “First Name”, “Last Name”, “Email”, date range for “Date of Last Order”, etc…
  • AJAX implementation so the whole page doesn’t refresh. Will work similar to how GridView worked in ASP.NET web forms if you put UpdatePanel around it.
Advertisements

Leave a Comment »

No comments yet.

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

Blog at WordPress.com.

%d bloggers like this: