Pagination in asp.net mvc core, entity framework and simplePagination.js

    0
    43

    Introduction

    This article will explain how to do pagination in asp.net mvc core application, targeting enity framework and using jquery template simplePagination.js.

    One of my application I had to implement pagination, there are lot’s of client side jquery templates are available, but the question is how to incorporate all those in to our application? This demo will walk through all the required bits and prices to do paging.

    A generic class is provided to handle all the calculation, including reading page size from app.config files.

    Background

    This demo is not taking about how to create a new asp.net core mvc project or any programing language. I am indented to solve a problem generally.

    Using the code

    The code will do the following.

    • Get records from a source(hard coded)
    • Using a pagination helper to calculate and get the requested page
    • Pass the page to the view
    • Ask the simplePagination.js to render the pager
    • done!!!

    Create a new .net core mvc application the project file would look like this

    <Project Sdk="Microsoft.NET.Sdk.Web">
    
 <PropertyGroup>
 <OutputType>exe</OutputType>
 <TargetFramework>netcoreapp1.1</TargetFramework>
 <ApplicationIcon />
 <OutputTypeEx>exe</OutputTypeEx>
 <StartupObject />
 </PropertyGroup>
 <ItemGroup>
 <Content Include="wwwroot\scripts\site.js" />
 </ItemGroup>
 <ItemGroup>
 <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.2" />
 <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.2" />
 <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.2" />
 <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
 <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" />
 <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
 <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.2" />
 </ItemGroup>
    </Project

    The startup.cs is setup to run mvc

    public class Startup
 {
 public Startup(IHostingEnvironment env)
 {
 var builder = new ConfigurationBuilder()
 .SetBasePath(env.ContentRootPath)
 .AddJsonFile("appsettings.json");
    
 Configuration = builder.Build();
 }
    
 private IConfiguration Configuration { get; set; }
    
 public void ConfigureServices(IServiceCollection service)
 {
 service.AddMvc();
    
 service.AddSingleton(Configuration)
 .AddTransient(typeof(IPageHelper<>), typeof(PageHelper<>))
 .AddSingleton<IPageConfig, PageConfig>();
 }
    
 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
 app.UseDeveloperExceptionPage();
    
 app.UseStaticFiles();
    
 app.UseMvc(config => config.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"));
    
 app.Run(handler => handler.Response.WriteAsync("Page not find."));
 }

    A home controller added to show list of employees

    HomeController will accept one constructor which will help preparing the pager. Usually pageHelper and _dbContext should be inside the business layer, for the readability I have using this directly inside controller.

    public class HomeController : Controller
 {
 private EmployeeDbContext _dbContext;
 private IPageHelper<EmployeeViewModel> _pageHelper;
    
 public HomeController(IPageHelper<EmployeeViewModel> pageHelper)
 {
 _dbContext = new EmployeeDbContext();
 _pageHelper = pageHelper;
 }
    
 [HttpGet]
 public IActionResult Index(int pageNumber = 1)
 {
 var allEmployees = _dbContext.GetEmployees();
    
 var result = _pageHelper.GetPage(allEmployees, pageNumber);
    
 var employees = new EmployeePageViewModel
 {
 Employees = result.Items,
 Pager = result.Pager
 };
    
 return View(employees);
 }
 }

    Three interfaces are defined, in that IResultSet<T> will be the return type from GetPage()

    public interface IPageHelper<T>
    { IResultSet<T> GetPage(IQueryable<T> items, int pageNumber);
    } public interface IPageConfig
    { int PageSize { get; }
    } public interface IResultSet<T>
    { IEnumerable<T> Items { get; set; } Pager Pager { get; }
    }
    

    And the model to store the page

    public class Pager
 {
 public int NumberOfPages { get; set; }
    
 public int CurrentPage { get; set; }
    
 public int TotalRecords { get; set; }
 }

    Finally the implementation of IPageHelper<T>

    Please note that GetPage accept a IQueryable<T> this can be changed to IEnumerable<T>

    public class PageHelper<T> : IPageHelper<T>
 {
 private readonly IPageConfig _pageConfig;
    
 public PageHelper(IPageConfig pageConfig)
 {
 _pageConfig = pageConfig;
 }
    
 public IResultSet<T> GetPage(IQueryable<T> items, int pageNumber)
 {
 var numberOfRecords = items.Count();
 var numberOfPages = GetPaggingCount(numberOfRecords, _pageConfig.PageSize);
    
 if (pageNumber == 0) { pageNumber = 1; }
    
 var pager = new Pager
 {
 NumberOfPages = numberOfPages,
 CurrentPage = pageNumber,
 TotalRecords = numberOfRecords
 };
    
 var countFrom = _countFrom(_pageConfig.PageSize, pageNumber);
    
 var resultSet = new ResultSet<T>
 {
 Pager = pager,
 Items = items.Skip(countFrom).Take(_pageConfig.PageSize)
 };
    
 return resultSet;
 }
    
 private readonly Func<int, int, int> _countFrom =
 (pageSize, pageNumber) => pageNumber == 1 ? 0 : (pageSize * pageNumber) - pageSize;
    
 private static int GetPaggingCount(int count, int pageSize)
 {
 var extraCount = count % pageSize > 0 ? 1 : 0;
 return (count < pageSize) ? 1 : (count / pageSize) + extraCount;
 }
    
 public class ResultSet<T> : IResultSet<T>
 {
 public IEnumerable<T> Items { get; set; }
 public Pager Pager { get; set; }
 }
 }

    Add a bower.json to the project and add the required dependencies.

    "dependencies": { "jquery": "3.2.1", "simplePagination.js": "*"
    }
    

    References to simplePagination.js and jquery.min.js are given to cshtml, and looping all the record using foreach

    Pagination is defined in <ul id=”emp-pagination” class=”pagination”></ul>

    This demo just contain one view so all the references are added here.

    @model PagerDemo.ViewModels.EmployeePageViewModel
    
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <link href="~/lib/simplePagination.js/simplePagination.css" rel="stylesheet" />
    <script src="~/lib/simplePagination.js/jquery.simplePagination.js"></script>
    <script src="~/scripts/site.js"></script>
    <table>
 <tr>
 <th>Id</th>
 <th>First Name</th>
 <th>Last Name</th>
 </tr>
 @foreach (var emp in Model.Employees)
 {
 <tr>
 <td>@emp.Id</td>
 <td>@emp.FirstName</td>
 <td>@emp.LastName</td>
 </tr>
 }
    </table>
    <ul id="emp-pagination" class="pagination"></ul>
    
    <input asp-for="Pager.NumberOfPages" type="hidden" id="hdnTotalNumberOfPages" value="@Model.Pager.NumberOfPages" />
    <input asp-for="Pager.CurrentPage" type="hidden" id="hdnCurrentPage" value="@Model.Pager.CurrentPage" />

    Next we will tell the somplepagination.js that id=”emp-pagination” is a pagger. Please note that #hdnTotalNumberOfPages and #hdnCurrentPage are getting from the view (hidden field)

    $(function () {
 $('#emp-pagination').pagination({
 pages: $('#hdnTotalNumberOfPages').val(),
 currentPage: $('#hdnCurrentPage').val(),
 itemsOnPage: 10,
 cssStyle: 'light-theme',
 onPageClick: function (pageNo) {
 var url = "/Home/Index?pageNumber=" + pageNo;
 window.location.href = url; 
 },
 hrefTextSuffix: '',
 selectOnClick: true
 });
    });

    When we run the application the result will be like this.

    History

    LEAVE A REPLY