Building a Sortable Paginated Table Component in ASP.NET MVC with PagedList

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

  1. Install the PagedList.Mvc package via NuGet. This automatically includes the PagedList dependency.
  2. 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.

Tags: ASP.NET MVC PagedList Pagination Ajax web development

Posted on Mon, 11 May 2026 01:45:23 +0000 by Procode