Overview
This article demonstrates how to create a sortable, paginated table component in ASP.NET MVC using the PagedList library. The implementation uses AJAX for seamless data updates without page reloads.
Prerequisites
- Install the PagedList.Mvc package via NuGet. This automatically includes the PagedList dependency.
- Include jquery.unobtrusive-ajax.min.js to enable asynchronous form submissions with Ajax.BeginForm.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.min.js"></script>
ViewModel Structure
The view model wraps the paginated data along with filter and sorting parameters:
public class ProductViewModel
{
public IPagedList<Product> Items { get; set; }
public string SearchTerm { get; set; }
public string CategoryFilter { get; set; }
public string SortOrder { get; set; }
public string PageSize { get; set; }
public int TotalRecords { get; set; }
}
Controller Implementation
The controller handles the initial request and subsequent AJAX calls:
public class ProductController : Controller
{
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository)
{
_repository = repository;
}
public ActionResult Index(string search, string category, string sort, int? page, int? size)
{
int pageNumber = page ?? 1;
int pageSize = size ?? 10;
string sortOrder = sort ?? "asc";
var products = _repository.GetFilteredProducts(search, category, sortOrder);
var pagedProducts = products.ToPagedList(pageNumber, pageSize);
var viewModel = new ProductViewModel
{
Items = pagedProducts,
SearchTerm = search,
CategoryFilter = category,
SortOrder = sortOrder,
PageSize = pageSize.ToString(),
TotalRecords = products.Count()
};
if (Request.IsAjaxRequest())
{
return PartialView("_ProductTable", viewModel);
}
return View(viewModel);
}
}
View with Ajax Form
The main view uses Ajax.BeginForm to enable asynchronous updates:
@model ProductViewModel
@using PagedList.Mvc
<div id="table-container">
@using (Ajax.BeginForm("Index", "Product",
new AjaxOptions
{
UpdateTargetId = "table-container",
InsertionMode = InsertionMode.Replace
}))
{
<div class="search-panel">
<input type="text" name="search" value="@Model.SearchTerm" placeholder="Search products..." />
<select name="category">
<option value="">All Categories</option>
<option value="electronics" selected="@(Model.CategoryFilter == "electronics")">Electronics</option>
<option value="clothing" selected="@(Model.CategoryFilter == "clothing")">Clothing</option>
</select>
<select name="sort">
<option value="asc" selected="@(Model.SortOrder == "asc")">Name A-Z</option>
<option value="desc" selected="@(Model.SortOrder == "desc")">Name Z-A</option>
<option value="price_asc" selected="@(Model.SortOrder == "price_asc")">Price Low-High</option>
<option value="price_desc" selected="@(Model.SortOrder == "price_desc")">Price High-Low</option>
</select>
<button type="submit">Apply Filters</button>
</div>
}
| ID | Name | Category | Price | Stock |
|---|---|---|---|---|
| @item.Id | @item.Name | @item.Category | @item.Price.ToString("C") | @item.Stock |
<div class="pagination">
@Html.PagedListPager(Model.Items,
page => Url.Action("Index", new {
search = Model.SearchTerm,
category = Model.CategoryFilter,
sort = Model.SortOrder,
page = page,
size = Model.PageSize
}),
PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
new AjaxOptions { UpdateTargetId = "table-container" }))
</div>
</div>
Key Implementation Details
Ajax Form Configuraton: Ajax.BeginForm submits the form asynchronously and updates the content within the UpdateTargetId element without a full page reload.
Parameter Matching: The form input names must correspond exactly to the action method parameters for model binding to work correctly.
PagedListPager Options: The pager supports AJAX replacement through PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing, wich wraps pager links with the appropriate AJAX attributes.
Partial View: The controller returns a partial view for AJAX requests, allowing only the table section to refresh while maintaining the rest of the page state.