Table sorting & pagination with jQuery and Razor in ASP.NET MVC

Introduction

jQuery enjoys living inside pages which are built on top of ASP.NET MVC Framework.

The ASP.NET MVC is a place where things are organized very well and it is quite hard to make them dirty, especially because the pattern enforces you on purity (you can still make it dirty if you want so ;) ).

We all know how easy is to build a HTML table with a header row, footer row and table rows showing some data. With ASP.NET MVC we can do this pretty easy, but, the result will be pure HTML table which only shows data, but does not includes sorting, pagination or some other advanced features that we were used to have in the ASP.NET WebForms GridView. Ok, there is the WebGrid MVC Helper, but what if we want to make something from pure table in our own clean style?

In one of my recent projects, I’ve been using the jQuery tablesorter and tablesorter.pager plugins that go along. You don’t need to know jQuery to make this work… You need to know little CSS to create nice design for your table, but of course you can use mine from the demo… So, what you will see in this blog is how to attach this plugin to your pure html table and a div for pagination and make your table with advanced sorting and pagination features.

 

Demo Project Resources

The resources I’m using for this demo project are shown in the following solution explorer window print screen:

  • Content/images – folder that contains all the up/down arrow images, pagination buttons etc. You can freely replace them with your own, but keep the names the same if you don’t want to change anything in the CSS we will built later.
  • Content/Site.css – The main css theme, where we will add the theme for our table too
  • Controllers/HomeController.cs – The controller I’m using for this project
  • Models/Person.cs – For this demo, I’m using Person.cs class
  • Scripts – jquery-1.4.4.min.js, jquery.tablesorter.js, jquery.tablesorter.pager.js – required script to make the magic happens
  • Views/Home/Index.cshtml – Index view (razor view engine)

the other items are not important for the demo.


ASP.NET MVC

1. Model

In this demo I use only one Person class which defines Person entity with several properties. You can use your own model, maybe one which will access data from database or any other resource.

Person.cs

public class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Email { get; set; }
    public int? Phone { get; set; }
    public DateTime? DateAdded { get; set; }
    public int? Age { get; set; }

    public Person(string name, string surname, string email,
        int? phone, DateTime? dateadded, int? age)
    {
        Name = name;
        Surname = surname;
        Email = email;
        Phone = phone;
        DateAdded = dateadded;
        Age = age;
    }
}

2. View

In our example, we have only one Index.chtml page where Razor View engine is used. Razor view engine is my favorite for ASP.NET MVC because it’s very intuitive, fluid and keeps your code clean.

3. Controller

Since this is simple example with one page, we use one HomeController.cs where we have two methods, one of ActionResult type (Index) and another GetPeople() used to create and return list of people.

HomeController.cs

public class HomeController : Controller
{
    //
    // GET: /Home/

    public ActionResult Index()
    {
        ViewBag.People = GetPeople();
        return View();
    }

    public List<Person> GetPeople()
    {
        List<Person> listPeople = new List<Person>();
        
        listPeople.Add(new Person("Hajan", "Selmani", "hajan@hajan.com", 070070070,DateTime.Now, 25));            
        listPeople.Add(new Person("Straight", "Dean", "email@address.com", 123456789, DateTime.Now.AddDays(-5), 35));
        listPeople.Add(new Person("Karsen", "Livia", "karsen@livia.com", 46874651, DateTime.Now.AddDays(-2), 31));
        listPeople.Add(new Person("Ringer", "Anne", "anne@ringer.org", null, DateTime.Now, null));
        listPeople.Add(new Person("O'Leary", "Michael", "23sssa@asssa.org", 32424344, DateTime.Now, 44));
        listPeople.Add(new Person("Gringlesby", "Anne", "email@yahoo.org", null, DateTime.Now.AddDays(-9), 18));
        listPeople.Add(new Person("Locksley", "Stearns", "my@email.org", 2135345, DateTime.Now, null));
        listPeople.Add(new Person("DeFrance", "Michel", "email@address.com", 235325352, DateTime.Now.AddDays(-18), null));
        listPeople.Add(new Person("White", "Johnson", null, null, DateTime.Now.AddDays(-22), 55));
        listPeople.Add(new Person("Panteley", "Sylvia", null, 23233223, DateTime.Now.AddDays(-1), 32));
        listPeople.Add(new Person("Blotchet-Halls", "Reginald", null, 323243423, DateTime.Now, 26));
        listPeople.Add(new Person("Merr", "South", "merr@hotmail.com", 3232442, DateTime.Now.AddDays(-5), 85));
        listPeople.Add(new Person("MacFeather", "Stearns", "mcstearns@live.com", null, DateTime.Now, null));

        return listPeople;
    }
}

 

TABLE CSS/HTML DESIGN

Now, lets start with the implementation. First of all, lets create the table structure and the main CSS.

1. HTML Structure

@{
    Layout = null;    
}
<!DOCTYPE html>
<html>
<head>
    <title>ASP.NET & jQuery</title>
    <!-- referencing styles, scripts and writing custom js scripts will go here -->
</head>
<body>
    <div>
        <table class="tablesorter">
            <thead>
                <tr>
                    <th> value </th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>value</td>
                </tr>
            </tbody>
            <tfoot>
                <tr>
                    <th> value </th>
                </tr>
            </tfoot>
        </table>
        <div id="pager">
            
        </div>
    </div>
</body>
</html>

So, this is the main structure you need to create for each of your tables where you want to apply the functionality we will create. Of course the scripts are referenced once ;).

As you see, our table has class tablesorter and also we have a div with id pager. In the next steps we will use both these to create the needed functionalities.

The complete Index.cshtml coded to get the data from controller and display in the page is:

<body>
    <div>
        <table class="tablesorter">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Surname</th>
                    <th>Email</th>
                    <th>Phone</th>
                    <th>Date Added</th>
                </tr>
            </thead>
            <tbody>
                @{
                    foreach (var p in ViewBag.People)
                    {            
                    <tr>
                        <td>@p.Name</td>
                        <td>@p.Surname</td>
                        <td>@p.Email</td>
                        <td>@p.Phone</td>
                        <td>@p.DateAdded</td>
                    </tr>
                    }
                }
            </tbody>
            <tfoot>
                <tr>
                    <th>Name</th>
                    <th>Surname</th>
                    <th>Email</th>
                    <th>Phone</th>
                    <th>Date Added</th>
                </tr>
            </tfoot>
        </table>
        <div id="pager" style="position: none;">
            <form>
            <img src="@Url.Content("~/Content/images/first.png")" class="first" />
            <img src="@Url.Content("~/Content/images/prev.png")" class="prev" />
            <input type="text" class="pagedisplay" />
            <img src="@Url.Content("~/Content/images/next.png")" class="next" />
            <img src="@Url.Content("~/Content/images/last.png")" class="last" />
            <select class="pagesize">
                <option selected="selected" value="5">5</option>
                <option value="10">10</option>
                <option value="20">20</option>
                <option value="30">30</option>
                <option value="40">40</option>
            </select>
            </form>
        </div>
    </div>
</body>

So, mainly the structure is the same. I have added @Razor code to create table with data retrieved from the ViewBag.People which has been filled with data in the home controller.

2. CSS Design
The CSS code I’ve created is:

/* DEMO TABLE */
body {
    font-size: 75%;
    font-family: Verdana, Tahoma, Arial, "Helvetica Neue", Helvetica, Sans-Serif;
    color: #232323;
    background-color: #fff;
}
table { border-spacing:0; border:1px solid gray;}
table.tablesorter thead tr .header {
    background-image: url(images/bg.png);
    background-repeat: no-repeat;
    background-position: center right;
    cursor: pointer;
}
table.tablesorter tbody td {
    color: #3D3D3D;
    padding: 4px;
    background-color: #FFF;
    vertical-align: top;
}
table.tablesorter tbody tr.odd td {
    background-color:#F0F0F6;
}
table.tablesorter thead tr .headerSortUp {
    background-image: url(images/asc.png);
}
table.tablesorter thead tr .headerSortDown {
    background-image: url(images/desc.png);
}
table th { width:150px;
           border:1px outset gray;
           background-color:#3C78B5;
           color:White;
           cursor:pointer;
}
table thead th:hover { background-color:Yellow; color:Black;}
table td { width:150px; border:1px solid gray;}


PAGINATION AND SORTING

Now, when everything is ready and we have the data, lets make pagination and sorting functionalities
1. jQuery Scripts referencing

<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.tablesorter.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.tablesorter.pager.js")" type="text/javascript"></script>

2. jQuery Sorting and Pagination script

 

<script type="text/javascript">
    $(function () {
        $("table.tablesorter").tablesorter({ widthFixed: true, sortList: [[0, 0]] })
        .tablesorterPager({ container: $("#pager"), size: $(".pagesize option:selected").val() });
    });
</script>

So, with only two lines of code, I’m using both tablesorter and tablesorterPager plugins, giving some options to both these.
Options added:

  • tablesorter - widthFixed: true – gives fixed width of the columns
  • tablesorter - sortList[[0,0]] – An array of instructions for per-column sorting and direction in the format: [[columnIndex, sortDirection], ... ] where columnIndex is a zero-based index for your columns left-to-right and sortDirection is 0 for Ascending and 1 for Descending. A valid argument that sorts ascending first by column 1 and then column 2 looks like: [[0,0],[1,0]] (source: http://tablesorter.com/docs/)
  • tablesorterPager – container: $(“#pager”) – tells the pager container, the div with id pager in our case.
  • tablesorterPager – size: the default size of each page, where I get the default value selected, so if you put selected to any other of the options in your select list, you will have this number of rows as default per page for the table too.

END RESULTS

1. Table once the page is loaded (default results per page is 5 and is automatically sorted by 1st column as sortList is specified)

2. Sorted by Phone Descending

3. Changed pagination to 10 items per page


4. Sorted by Phone and Name (use SHIFT to sort on multiple columns)

5. Sorted by Date Added

6. Page 3, 5 items per page

 

ADDITIONAL ENHANCEMENTS

We can do additional enhancements to the table. We can make search for each column. I will cover this in one of my next blogs. Stay tuned.

DEMO PROJECT

You can download demo project source code from HERE.


CONCLUSION

Once you finish with the demo, run your page and open the source code. You will be amazed of the purity of your code.

Working with pagination in client side can be very useful. One of the benefits is performance, but if you have thousands of rows in your tables, you will get opposite result when talking about performance. Hence, sometimes it is nice idea to make pagination on back-end. So, the compromise between both approaches would be best to combine both of them. I use at most up to 500 rows on client-side and once the user reach the last page, we can trigger ajax postback which can get the next 500 rows using server-side pagination of the same data. I would like to recommend the following blog post http://weblogs.asp.net/gunnarpeipman/archive/2010/09/14/returning-paged-results-from-repositories-using-pagedresult-lt-t-gt.aspx, which will help you understand how to return page results from repository.

I hope this was helpful post for you. Wait for my next posts ;).

Please do let me know your feedback.

Best Regards,
Hajan

28 Comments

  • I would like to see the same example but without using jquery.

    The bad thing is that you need to retrieve all data in first time.

  • @kalvin, the main point of this example is to verify few things

    - How easy is to create table from server-side data in ASP.NET MVC

    - The purity of the code when using @Razor

    - How to use jQuery to make such enhancements

    So, since we mainly do sorting and pagination on client-side, jQuery / JavaScript have to be used.

    As for retrieving all data in first time, please read the blog more carefully and you will sum up that I have noted this as a performance issue (if you have thousands of rows) and have proposed very good solution to that ;).

    Hope this helps.

  • Yes, you have right.

    Can you make a sample like that above but without using jQuery? I was looking today some example with pure asp.net mvc and list with pagging, sorrting, searching and i could not find anything like that.
    I only found many examples but with using jquery.
    Maybe my request is strange, but i'm asp.net web forms developer, and it's hard for me to understand how to make that simple list in mvc. In web forms i allways using telerik controls and i have never bother how it works, but in mvc i think that i should know how to do that.
    Thanks in advance and sorry for my language.

  • @kalvin, don't worry ;)!

    Well, ASP.NET MVC works different comparing to ASP.NET WebForms. But, you still have some ways to make it quickly. For example, there are Telerik controls for ASP.NET MVC too...

    Check this: http://demos.telerik.com/aspnet-mvc/grid?theme=vista

    Also you may want to look at the MvcContrib Grid, which supports sorting and pagination too - link: http://mvccontrib.codeplex.com/wikipage?title=Grid

    Hope these will help you.

    If you experience any problems, I'm thinking to write a blog post that will show some examples on howto use them ;).

    Cheers...

  • i like it

    Table sorting &amp;amp; pagination with jQuery and Razor in ASP.NET MVC - Hajans Blog

    now im your rss reader

  • Thanks, I think I need analize more examples with mvc and jquery to change attitude for developing web application in asp.net mvc.

    I used telerik mvc control in elder version and it was terrible - i'm saying about grid control with grouping(but other controls had a lot of mistakes), but now i see on examples and i think that they correct most of problems.

  • @rbg, thanks.

  • @kalvin, in ASP.NET MVC you have to think a little deeper and pay attention on more details. The Telerik grid control works fine now. You can also try the MvcContrib grid, which is also fine. Once you get comfortable working with ASP.NET MVC, you will be impressed how much you are going to like it ;). It really keeps the things clean and gives you the power to have full control on everything.

    Good luck and wish you success in your projects.

  • Thanks a lot. I really appreciate your help. I will view your blog frequently.

  • Hi,
    If i am in second page(Paging) of table and click the sorting then it sort the entire table. So the row values in my 2nd page gets changed. Is there any way to sort only the values in the current page of the table

  • Thankyou Hajan for a very clear and concise description of an elegant solution.

  • @San, yes, but the pager plugin doesn't work that way... It sorts the whole table and shows the data on the current page you are. I think you will need to handle that yourself, except if there is an upgrade of the pagination plugin for which you can check on the author's site.

  • Dennis, thank you for your nice comment ;)

  • Good article, thanks. I'm having slight difficulty with the pager css. I have the table in a div with a white background, and I want the pager contained on the same div. At the moment it overlaps the border of the div and I can't seem to fix it...

  • Thanks for posting this. It is an enormous help to me. I added a checkbox that, when checked, will hide some of the rows based on a value in one of the columns. What function within the tablesorter javascript should I call to get the table to refresh so that it will still be showing n rows instead of n-# that were just hidden? Thanks.

  • Hi Stephen,

    Please check this: http://weblogs.asp.net/hajan/archive/2011/08/20/trigger-update-to-jquery-tablesorter-on-dynamically-modified-table.aspx - where I have shown how to trigger update if any changes are made to the set of data bound to the table with tablesorter. If I understand your question well, this post will give you some ideas...

    Hope this is helpful.

  • Hi All,
    Nice post but can i see a live demo

  • @Arun Jain, There is download link for the project. You can get it for free ;)

  • I am getting this error when I try to change the page size.

    Microsoft JScript runtime error: 'settings' is null or not an object

    It takes me to jquery.validate.js. Any ideas, what might be causing it?

    Thanks!

  • How about on a partial view...nothing seems to fire. I added an alert to the tablesort js file to see that it gets called, bt I get no header images and no sorting.

  • This is just what I need, damn I have used so many stupid solutions with asp.net mvc and web-grid, waste of time. jQuery is the future. Thx for the tutorial :)

  • I tried to use your solution and every time i got the same error table.config is undefined (jquery.tablesorter.pager.js) Line 11. I don't know what else to do.

  • thank for posting. fantastic solution

  • Is it possible to ignore hidden rows for tablesorterpager rows count?

  • Thanks,,,,,,,,,,,,,,,,,really good and best solution.

    --------------------a example for convert VAR to LIST&lt;&gt;------------------------

    &nbsp;public ActionResult Index()

    &nbsp; &nbsp; &nbsp; &nbsp;{

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ViewBag.People = GetEmployees();

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return View();

    &nbsp; &nbsp; &nbsp; &nbsp;}

    &nbsp; &nbsp; &nbsp; &nbsp;public List&lt;dbTable&gt; GetEmployees()

    &nbsp; &nbsp; &nbsp; &nbsp;{

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;var db = new TestClassDataContext();

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;var result = from E in db.tblEmployees

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select new

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EmpId = E.dbEmpId,

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EmpName = E.dbName,

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ContactNo = E.dbContactNo,

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; date=E.dbDOB

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;List&lt;dbTable&gt; query = new List&lt;dbTable&gt;();

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;foreach (var d in result)

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;query.Add(new dbTable(d.EmpId, d.EmpName, d.ContactNo, d.date));

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}

    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return query;

    &nbsp; &nbsp; &nbsp; &nbsp;}

  • It was not working when page is under layout page. What is the problem?

  • Thanks a lot Hajan. Its really a good article!!

  • it is a very good example its very helps to me

Comments have been disabled for this content.