This post was revised and updated on 10/24/2020.
If you fell on this post first, you’ll notice that the implementation of paging is a little more involved than the average example on the web. That’s because most of the other examples are either, limited, or outright “The wrong way to do it”. To start from the begining, click here: The Ultimate Guide to MVC Paging!.
Once you have a PagedResult object, or any object with the collection and total count of records that match the filters, back from your web service, you’ll need to convert it to a SearchModel that can be used throughout your MVC views and partial views to enable the paging.
In the implementation demo, we’re going to call a function to return the SearchModel, both from the original call to the Index controller, and then later from a Ajax when the user clicks on a page number or sort column. So I create a function that both controllers can share:
/// <summary>
/// Category search with paged results
/// </summary>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <param name="searchTerm"></param>
/// <param name="sortFilter"></param>
/// <param name="sortColumn"></param>
/// <param name="sortDescending"></param>
/// <param name="ActiveOnly"></param>
/// <returns></returns>
public async Task<SearchModel<LuCategory>> GetCategoriesPagedAsync(int page = 1,
int pageSize = 25,
string searchTerm = "",
string searchFilter = "",
string sortColumn = "ShortName",
bool sortDescending = false,
bool ActiveOnly = true)
{
SearchModel<LuCategory> _smlus = new SearchModel<LuCategory>();
try
{
PagedResult<LuCategory> _cats = await LookupsSvc.GetCategoriesPagedAsync(page, pageSize, searchTerm, searchFilter, sortColumn, sortDescending, ActiveOnly);
_smlus.TotalItems = _cats.TotalCount;
_smlus.CurrentPage = page;
_smlus.PageSize = pageSize;
_smlus.RecordCount = _cats.Items.Count();
_smlus.SortColumn = sortColumn;
_smlus.SearchTerm = searchTerm;
_smlus.SortDescending = sortDescending;
_smlus.ActiveOnly = ActiveOnly;
_smlus.SortedResults = _cats.Items.ToList();
_smlus.actionName = "CatDisplay";
_smlus.controllerName = "Lookups";
return _smlus;
}
catch (Exception)
{
throw;
}
}
While the code might be self explanatory, I’ll explain what’s happening here. Notice the two properties, actionName and contollerName. These are used by the partial views to know how to get to the controller method that’s going to serve up the partial view, so you can set it to go anywhere you like. The reason these properties are part of the object, is because this allows the paging partial views to be generic and reusable.
We would normally return the PagedResults object from our web service call. Then I transfer the results into the SearchModel while appending all the search, paging and filtering criteria passed into the method. The reason for this is that we want to retain this data in order to use the SearchModel in the Index view, as well as the partial views used for paging.
So lets go over the controllers first. The Index GET has all the parameters needed for the SearchModel with defaults, so that it can be called the first time with no arguments.
/// <summary>
/// Index of categories paged
/// </summary>
/// <param name="page"></param>
/// <param name="pageSize"></param>
/// <param name="searchTerm"></param>
/// <param name="sortFilter"></param>
/// <param name="sortColumn"></param>
/// <param name="sortDescending"></param>
/// <param name="ActiveOnly"></param>
/// <returns></returns>
[HttpGet]
public async Task<IActionResult> Index(int page = 1, int pageSize = 25, string searchTerm = "", string sortFilter = "", string sortColumn = "ShortName", bool sortDescending = false, bool ActiveOnly = true)
{
try
{
var _smlus = await GetCategoriesPagedAsync(page, pageSize, searchTerm, sortFilter, sortColumn, sortDescending, ActiveOnly);
return View(_smlus);
}
catch (Exception)
{
throw;
}
}
Notice how having all the code in a sub method keeps our controller method clean and uncluttered.
Next, we need to have the controller method that returns the PartialView that will service the Ajax calls:
/// <summary>
/// Edit Category, with display of items
/// </summary>
/// <param name="id"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> Edit(long id, int pageSize = 25)
{
LuCategoryModel model = new LuCategoryModel();
try
{
if (id >= 0)
{
// Get the base LuCategory
LuCategory _return = await LookupsSvc.GetLuCategoryByIdAsync(id);
model = Helpers.MappingUtils.LuCatToLuCatModel(_return, false);
// Get a paged result of some items because we want a paged list of child items as well
model.Items = await GetItemsPagedAsync(id, 1, pageSize);
}
return View(model);
}
catch (Exception)
{
throw;
}
}
This is all that’s needed to service the Index view, grid partial view, page count partial view and the pager partial view.