Displaying Search Results In A WebGrid

A number of people have run into problems when trying to combine a search or filter form, and a WebGrid. The main issue that arises is when paging or sorting the search result or a filtered subset of it. Here, I look at the cause of the problem and what you can do about it.

Like other articles in this series, the sample code makes use of a SQL CE 4.0 version of the Northwind database. It is available as part of the download that accompanies this article, a link to which is provided at the end. The sample also makes use of the same layout page as other samples, which references jQuery, and includes a RenderSection call to an optional section called "script":

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />

        <title>@Page.Title</title>
        <script src="@Href("~/scripts/jquery-1.6.2.min.js")" type="text/javascript"></script>

        <link href="@Href("~/styles/site.css")" rel="stylesheet" />
        @RenderSection("script", required: false)
    </head>

    <body>
        @RenderBody()
    </body>
</html>

The main file in the sample includes a code block at the top, the HTML and Razor markup, and content for the "script" section:

@{
    Page.Title = "Filter WebGrid";
    var db = Database.Open("Northwind");
    var query = "SELECT DISTINCT Country FROM Customers ORDER BY Country";
    var countries = db.Query(query);
    query = "SELECT * FROM Customers  WHERE CompanyName LIKE @0 AND Country LIKE @1";
    var company = "%" + Request["company"] + "%";
    var country = "%" + Request["country"] + "%";
    var data = db.Query(query, company, country);
    var columns = new[]{"CustomerID", "CompanyName", "ContactName", "Address", "City", "Country", "Phone"};
    var grid = new WebGrid(data, columnNames: columns, rowsPerPage: 6);
}
<h1>Filter WebGrid</h1>
<form method="post">
    <div id="grid">
        Company Name: <input type="text" name="company" value="@Request["company"]" />
        Country: <select name="country">
                 <option></option>   
            @foreach(var item in countries){
                <option @(Request["country"] == item.Country ? " selected=\"selected\"" : "")>@item.Country</option>
            }
        </select>
        <input type="submit" />
        @grid.GetHtml(    
            tableStyle : "table",
            alternatingRowStyle : "alternate",
            headerStyle : "header",
            columns: grid.Columns(
                grid.Column("CustomerID", "ID"),
                grid.Column("CompanyName", "Company"),
                grid.Column("ContactName", "Contact"),
                grid.Column("Address"),
                grid.Column("City"),
                grid.Column("Country"),
                grid.Column("Phone")
            )
        )
    </div>
</form>
@section script{
<script type="text/javascript">
   $(function(){
        $('th a, tfoot a').live('click', function() {
            $('form').attr('action', $(this).attr('href')).submit();
            return false;
        });
    });
</script>
}


The page features a form containing a text box and a select list along with the grid:

By default, the grid is populated by all companies within the database. There are a couple of parameters in the WHERE clause, but the value of the parameters on the first request is %%, which equates to a wildcard match.

The text box allows the user to search for entries based on part of a company name, while the select list allows the user to filter results in the grid by country. The form is POSTed to the server, and therein lies the root of the problem. You can see that sorting and paging links feature are part of the grid. All of these, when clicked, result in GET requests being made, which leaves the form - and its content - behind.

The answer to the problem lies in the snippet of jQuery that appears in the script section. A handler is attached to the onclick event of the links in the table head and table foot areas - or the sorting and paging links. When they are clicked, the value of the link is obtained and provided to the form's action attribute, Then the form is submitted using POST, and the GET request is cancelled by return false. This ensures that paging and sorting information is preserved in the Request.QueryString collection, while any form field values are passed in the Request.Form collection.

A demo containing the source code is available here.