.NET Unplugged

July 17, 2009

ASP.NET MVC Grid – Part 6 – Advanced Search, AJAX, Users with Javascript Disabled

Filed under: ASP.NET MVC — Eric P @ 8:06 am

In this part I will implement:

  • advanced search
  • using AJAX for grid actions
  • support for users with Javascript disabled
  • separate grid.css and grid.js

Click image to view the full screenshot

Click image to view the full screenshot


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

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

Implementation

1. Advanced Search
Advanced Search differs from Keyword Search in a way that users can search for customers using one or more fields, for example – find all customers with first name “John” and with last order placed in the last month.

To implement advanced search I extended SearchForm class to include fields like First Name, Last Name, Phone, etc…


public class SearchForm
{
	//Properties
	public bool IsAdvanced { get; set; }
	public string Keyword { get; set; }
}

public class CustomerSearchForm : SearchForm
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string Email { get; set; }
	public string Phone { get; set; }

	public DateTime? FromDateOfLastOrder { get; set; }
	public DateTime? ToDateOfLastOrder { get; set; }
}

To be able to access Advanced search fields in View through a model, I passed CustomerSearchForm as generic when declaring grid:



public class CustomerController : Controller
{
        private Grid<Customer, CustomerSearchForm> _grid;
        ...
}

public class Grid<TEntity, TSearchForm> where  TSearchForm : SearchForm, new()
{
	private Pager _pager;
	private Sorter _sorter;
	private TSearchForm _searchForm;
    
   

This way I could use:

<td>
	First Name<br />
	<%= Html.TextBox("SearchForm.FirstName", null, new { @style = "width: 100px", @maxlength = "20" })%>
</td>

otherwise binder had issue binding to correct properties.

Then I added some animation with help of JQuery – to switch between Keyword and Advanced searches. See more about this in “Observations” section.

2. AJAX for Grid Actions
This functionality was very easy to implement in Web Forms. You just had to put UpdatePanel around the GridView and all paging and sorting would be asynchronous.

In MVC, it is a little more complicated, but with a benefit of a more control and flexibility. I moved Grid html (without search form) into a partial class “_Grid.aspx”. Then I used JQuery AJAX functionality and BlockUI plugin to perform all page actions without refreshing the whole page.


jQuery(document).ready(function() {

	initGrid();

 	//Intercept form submit and do submission with AJAX
	$("#grid").parents("form:first").submit(function() {
		submitFormWithAjax();
		return false;
	});
...
}); 


function submitFormWithAjax() {

	var form = $("#grid").parents("form:first");
	var formData= form.serialize();         //Serialize form control values to be passed asynchronously

	block();   //use BlockUI to display "Processing ... " popup

	$.ajax({
		type: "POST",
		url: "_Grid",           //Partial view
		data: formData,
		success: function(newHtml) {
			//Use timeout of half a second to see "Processing..." animation, otherwise it goes by too quickly.
			setTimeout(function() {
							$("#grid").html(newHtml);
	
							initGrid();
							unblock(); }, 500);
		},

		error: function(request, textStatus, errorThrown) {
			alert("AJAX error: " + request.statusText);
			unblock();
		}
	});
}

function block() {
	$('#grid #grid-data').block({
		message: '<div class="blockUI-message">Processing...</div>',
		css: { border: 'none', width: 'auto' },
		overlayCSS: { backgroundColor: '#C0C0C0' }
	});
}

function unblock() {
	$('#grid #grid-data').unblock();
}

3. Support for users with javascript disabled
This feature allows users with Javascript disabled to use most of the Grid’s functionality. Surprisingly, there are still 5% of users who have JS disabled for variety of reasons (http://www.w3schools.com/browsers/browsers_stats.asp).

For Paging and Sorting, I previously submitted the form using javascript, so the links would look like:

//paging
<a href="#" onclick="goToPage(2)">&gt;</a> 


//sorting
<th>
	<a href="#" class="" onclick="sort('FirstName', 'Asc');">First Name</a>
</th>

//with javascript used to set appropriate hidden variables and grid action, then submit the form
function goToPage(pageIndex) {
	$("#Pager_CurrentPage").val(pageIndex);
	$("#GridAction").val("GoToPage");

	submitForm();
}

function sort(sortField, sortDirection) {
	$("#GridAction").val("Sort");
	
	$("#Sorter_SortField").val(sortField);
	$("#Sorter_SortDirection").val(sortDirection);

	submitForm();
}

For this to work with JS disabled, i replaced “#” in href attribute with a url containing the whole grid state as well as appropriate paging/sorting action. So now, paging and sorting links look like:


//Paging
<a href="/Customer/List?Pager.CurrentPage=2&Pager.PageSize=5&Sorter.SortField=ID&Sorter.SortDirection=Asc&SearchForm.IsAdvanced=False&SearchForm.Keyword=&GridAction=GoToPage" onclick="goToPage(2); return false;">&gt;</a>

//Sorting
<a class="" href="/Customer/List?Pager.CurrentPage=1&Pager.PageSize=5&Sorter.SortField=FirstName&Sorter.SortDirection=Asc&SearchForm.IsAdvanced=False&SearchForm.Keyword=&GridAction=Sort" onclick="sort('FirstName', 'Asc'); return false;">First Name</a>

Notice that onclick now has a “return false;” statement. This suppresses default link action – which is to go to URL specified in HREF.

Internally this is implemented using Grid.GetQueryString and Grid.GenUrl methods, that are called from methods used to generate links – Grid.PageNavActionLink(…) and Grid.SortActionLink(…).

One nice side-effect from using this approach is that a Search Engine crawler like Google crawler should be able to crawl all the data in the grid using page/sort URLs.

For Page Size, I added “Refresh” button which is only displayed if JS is disabled:

<noscript><input type="submit" id="refresh-button" value="refresh" /></noscript>

One feature that doesn’t work yet for “JS disabled” mode is switching Search to Advanced Form. I will get it to work in next part.

4. Separate grid.js and grid.css
It is usually a bad practice to keep big chunks of javascript and CSS inline on the actual page. To correct this issue, I moved all grid javascript and styles into separate files.

JS is now in ‘/Scripts/grid.js’.
CSS is now in ‘/Content/grid.css’.

This will also help with re-usability.

Observations

  • When adding conditions for Advanced search I discovered that C# String.Contains method doesn’t have an option for case-insensitive comparison. To do that I added string extensions from the following link:

    http://schleichermann.wordpress.com/2009/02/24/c-stringcontains-case-insensitive-extension-method/

    public static class StringExtensions
    {
    	public static bool Contains(this String source, String value, StringComparison comparison)
    	{
    		return source.IndexOf(value, comparison) != -1;
    	}
        
    	public static bool ContainsCaseInsensitive(this String source, String value)
    	{
    		return Contains(source, value, StringComparison.CurrentCultureIgnoreCase);
    	}
    }
    
  • When I worked on animation for hiding/showing advanced search, I noticed that animations ran asynchronously, so sometimes it would fully skip the first animation if the second animation was called right away.

    Originally the code looked like this:

    $("#search-form #advanced-link").click(function() {
    
    	//Hide keyword search form using slide up animation
    	$("#search-form").slideUp("medium");
    
    	//Hide/show appropriate links and form fields
    	...
    	
    	//Display advanced search form using slide down animation
    	$("#search-form").slideDown("medium");
    
    	...
    });
    

    Since slideUp was asynchronous, slideDown would get called even before slideUp would get started, so all I would see was the Keyword form disappearing (without animation) and Advanced search appearing with animation.

    To make sure that slideUp animation finished before calling slideDown – I changed the code to call slideDown in slideUp’s callback argument. Final code looks like this:

    $("#search-form #advanced-link").click(function() {
    	$("#search-form").slideUp("medium", function() {
    		...
    		$("#search-form").slideDown("medium");
    	});
    
    	...
    });
    

Coming up…

Next will be the biggest task of them all. Making grid into re-usable control…

About these ads

2 Comments »

  1. When you write data in a search text box in the advanced search, then click the sorting link, the grid is both sorted and filtered by the criteria in the search text box.

    It should be sorted only and shouldn’t be filtered when the sort link is clicked, even if the search text box contains value.

    Comment by mohamed3asem — January 14, 2010 @ 9:21 am | Reply

  2. Hello friends

    Someone has the same example but in MVC 3 Razor?

    blessings

    Comment by CARLOS ALVARADO (@CadavidAlvarado) — April 12, 2012 @ 6:57 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 Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: