Introduction
One of the task a developer need to deal with when implementing an ASP.NET MVC application is writing pagination for data lists that simulates the paging behavior of typical ASP.NET application with ASP web Server controls (i.e. GridView, DataGrid etc). You need to use the html controls and HtmlHelper methods and device your own mechanism to implement the pagination behavior. In this article I am going to explain you about a simple approach of implementing paging in an ASP.NET MVC application (with Razor) with minimal coding and with the use of scripting technologies like ajax an jquery.
Using the code
The complete application contains the following layers
- Database: I have used the Northwind sample database in SQL Server and for simplicity focused on implementing data manipulation scenario for a single reference table (Categories table)
- Object Relational Mapping: I have used Entity Framework for ORM considering the ease of use and less complexity of the current scenario (However you can use other ORM technologies like NHibernate or IBATIS.NET that offers more capabilities on implementing advance mappings)
- UI: The controllers will use the context methods from the entity model and manipulate data in the database and present them in the UI.
The Solution at a glace
The User clicks on the "Previous", "Next" buttons. The UI will call the corresponding action method which will use 1 parameter passed from front end, based on the current page number. The action method constructs the required parameters (pointers for start and end indexes of the required record set), make a call to the entity model. The entity model use a Stored Procedure call with the specified parameter and return the requested record set.
UI
The View
The view has the following controls for pagination. Note that hidden input is used to persist current page index value which sets in the controller.
<input id="btnPrevious" type="button" value="Previous" />
<input id="btnNext" type="button" value="Next" />
<input id="currentPageIndex" type="hidden" value=@ViewBag.CurrentPageIndex />
The following jQuery methods perform an asynchronous ajax request to the corresponding action methods in the controller. Note that on success the response returned from the action method is rendered to the <body>.
$("#btnNext").click(function () {
$.ajax({
url: 'Category/Next',
data: { currentPageIndex: document.getElementById('currentPageIndex').value },
success: function (response) {
$("body").html(response);
}
});
});
$("#btnPrevious").click(function () {
$.ajax({
url: 'Category/Previous',
data: { currentPageIndex: document.getElementById('currentPageIndex').value },
success: function (response) {
$("body").html(response);
}
});
});
The current page index is retrieved from the controller and it will be used to determine whether the first or last page has been selected and enable/disable the buttons accordingly.
@if (ViewBag.CurrentPageIndex > 1)
{
@:$("#btnPrevious").attr("disabled");
}
else
{
@:$("#btnPrevious").attr("disabled", true);
@:$("#btnPrevious").css("color", "Gray");
}
@if (ViewBag.CurrentPageIndex < ViewBag.LastPageIndex)
{
@:$("#btnNext").attr("disabled");
}
else
{
@:$("#btnNext").attr("disabled", true);
@:$("#btnNext").css("color", "Gray");
}
Controller Actions
The corresponding action methods computes the new start/end page indexes, add to the ViewBag and render the "Index" view.
public ActionResult Next(int currentPageIndex)
{
int startIndex = (currentPageIndex * PageSize) + 1;
int endIndex = startIndex + PageSize - 1;
ViewBag.CurrentPageIndex = currentPageIndex + 1;
ViewBag.LastPageIndex = this.getLastPageIndex();
return View("Index", db.CategoriesSelectByRange(startIndex, endIndex));
}
public ActionResult Previous(int currentPageIndex)
{
currentPageIndex -= 1;
int endIndex = currentPageIndex * PageSize;
int startIndex = endIndex - (PageSize - 1);
ViewBag.CurrentPageIndex = currentPageIndex;
ViewBag.LastPageIndex = this.getLastPageIndex();
return View("Index", db.CategoriesSelectByRange(startIndex, endIndex));
}
ORM & Database
The Entity model contains the Category entity. Now we need to pass 2 parameters to the entity model and retrieve the list of categories by the parameters. A stored procedure will return the list of categories by the start index and end index. You can use the "Function Imports" feature in the "Model Browser" that will guide through the process of creating a function that maps to a stored procedure (Refer the Function Import window below).
Note that you need to create the stored procedure and update the entity model before this step.
You can use either of the return collection types based on the requirement, in here I have used "Category" entity as I used "Index" view in this scenario and it is strongly typed with "Category" entity.
Stored procedure accepts startIndex and endIndex as parameters and returns Categories between the parameters. I have used ROW_NUMBER() built in function to filter the return data list.
WITH CategoriesSubSet AS
(SELECT ROW_NUMBER() OVER(ORDER BY [CategoryID]) AS rowIndex,
[CategoryID],
[CategoryName],
[Description],
[Picture]
FROM [dbo].[Categories])
SELECT [CategoryID],
[CategoryName],
[Description],
[Picture]
FROM CategoriesSubSet
WHERE rowIndex >= @startIndex AND rowIndex <= @endIndex
Summary
This is a very simple implementation of pagination in an MVC web application and it use a simple straight forward approach with minimal coding effort.
Possible Enhancements
It is worthwhile to spend some time to encapsulate the pagination into a partial view (corresponds to user controls in ASP.NET standard web controls) so that pagination can be used across multiple views. Another enhancement is implementing sorting for the lists.