Building pager component for ASP.NET Core

gpeipman
28.3K views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

Building pager component for ASP.NET Core

This example shows how to build generic pager component for ASP.NET Core. Although the example focuses on Entity Framework Core it is possible to use the same pager view component with whatever data until you provide correct paging data.

Sample entity and database context

Here is the simple Contact entity and database context used in this demo. It uses in-memory implementation of Entity Framework Core provider but it can be used with all other providers too.

public class Contact
{
    [Key]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}

public class SampleDbContext : DbContext
{
    public DbSet<Contact> Contacts { get; set; }

    public SampleDbContext(DbContextOptions<SampleDbContext> options) : base(options)
    {
    }

    public void GenerateSampleData()
    {
        Contacts.Add(new Contact { Id = 1, FirstName = "John", LastName = "Doe", Email = "john@example.com" });
        Contacts.Add(new Contact { Id = 2, FirstName = "Peter", LastName = "Jones", Email = "peter@example.com" });
        Contacts.Add(new Contact { Id = 3, FirstName = "Mary", LastName = "Smith", Email = "mary@example.com" });
        Contacts.Add(new Contact { Id = 4, FirstName = "Ian", LastName = "Green", Email = "ian@example.com" });
        Contacts.Add(new Contact { Id = 5, FirstName = "Nancy", LastName = "Brownwood", Email = "nancy@example.com" });
        Contacts.Add(new Contact { Id = 6, FirstName = "Tommy", LastName = "High", Email = "tommy@example.com" });
        Contacts.Add(new Contact { Id = 7, FirstName = "Gabriel", LastName = "Santos", Email = "gabriel@example.com" });
        Contacts.Add(new Contact { Id = 8, FirstName = "Ryan", LastName = "James", Email = "ryan@example.com" });
        Contacts.Add(new Contact { Id = 9, FirstName = "Marc", LastName = "James", Email = "marc@example.com" });
        Contacts.Add(new Contact { Id = 10, FirstName = "James", LastName = "Dallas", Email = "james@example.com" });
        Contacts.Add(new Contact { Id = 11, FirstName = "Ron", LastName = "Steer", Email = "ron@example.com" });
        Contacts.Add(new Contact { Id = 12, FirstName = "Liam", LastName = "Schwarz", Email = "liam@example.com" });

        SaveChanges();
    }
}

Paged data classes

There are two classes for paged data:

  • PagedResult - class for typed data paged
  • PagedResultBase - base class for paging, used in UI layer
public abstract class PagedResultBase
{
    public int CurrentPage { get; set; }
    public int PageCount { get; set; }
    public int PageSize { get; set; }
    public int RowCount { get; set; }

    public int FirstRowOnPage
    {

        get { return (CurrentPage - 1) * PageSize + 1; }
    }

    public int LastRowOnPage
    {
        get { return Math.Min(CurrentPage * PageSize, RowCount); }
    }
}

public class PagedResult<T> : PagedResultBase where T : class
{
    public IList<T> Results { get; set; }

    public PagedResult()
    {
        Results = new List<T>();
    }
}

LINQ extension for paging Entity Framework Core data

There is LINQ extension method that can be called on whatever IQueryable that translates to correct SQL by underlying database provider.

public static class LinqExtensions
{
    public static PagedResult<T> GetPaged<T>(this IQueryable<T> query,
                                                int page, int pageSize) where T : class
    {
        var result = new PagedResult<T>();
        result.CurrentPage = page;
        result.PageSize = pageSize;
        result.RowCount = query.Count();

        var pageCount = (double)result.RowCount / pageSize;
        result.PageCount = (int)Math.Ceiling(pageCount);

        var skip = (page - 1) * pageSize;
        result.Results = query.Skip(skip).Take(pageSize).ToList();

        return result;
    }
}

Building view component

Our generic view component is simple class that takes PagedResultBase and uses it as model in related view.

public class PagerViewComponent : ViewComponent
{
    public Task<IViewComponentResult> InvokeAsync(PagedResultBase result)
    {
        return Task.FromResult((IViewComponentResult)View("Default", result));
    }
}

Main work for generating pager in UI is done in Default view of view component.

@model PagedResultBase
@{
    var urlTemplate = Url.Action() + "?page={0}";
    var request = ViewContext.HttpContext.Request;
    foreach (var key in request.Query.Keys)
    {
        if (key == "page")
        {
            continue;
        }

        urlTemplate += "&" + key + "=" + request.Query[key];
    }

    var startIndex = Math.Max(Model.CurrentPage - 5, 1);
    var finishIndex = Math.Min(Model.CurrentPage + 5, Model.PageCount);
}

<div class="row">
    <div class="col-md-8 col-sm-8 items-info">
        Items @Model.FirstRowOnPage to @Model.LastRowOnPage of @Model.RowCount total
    </div>
</div>
<div class="row">
    <div class="col-md-8 col-sm-8">
        @if (Model.PageCount > 1)
        {
            <ul class="pagination pull-right">
                <li><a href="@urlTemplate.Replace("{0}", "1")">&laquo;</a></li>
                @for (var i = startIndex; i <= finishIndex; i++)
                {
                    @if (i == Model.CurrentPage)
                    {

                        <li><span>@i</span></li>
                    }
                    else
                    {

                        <li><a href="@urlTemplate.Replace("{0}", i.ToString())">@i</a></li>
                    }
                }
                <li><a href="@urlTemplate.Replace("{0}", Model.PageCount.ToString())">&raquo;</a></li>
            </ul>}
    </div>
</div>

Invoking pager view component

Pager view component can be used in every view where we have some PagedResultBase available. Let's see Index view of Home controller where table with paged data is displayed.

@model PagedResult<Contact>
@{
    ViewData["Title"] = "Home Page";
}

<h2>Entity Framwork Core pager example</h2>
<div class="row">
    <div class="col-md-5">
        <table class="table table-bordered table-hover">
            <thead>
                <tr>
                    <th>#</th>
                    <th>First name</th>
                    <th>Last name</th>
                    <th>E-mail</th>
                </tr>
            </thead>
            <tbody>
                @{ var i = 1; }
                @foreach (var contact in Model.Results)
                {
                    var rowNo = (Model.CurrentPage - 1) * Model.PageSize + i;
                    i++;
                    <tr>
                        <td>@rowNo</td>
                        <td>@contact.FirstName</td>
                        <td>@contact.LastName</td>
                        <td>@contact.Email</td>
                    </tr>
                }
            </tbody>
        </table>

        @(await Component.InvokeAsync<PagerViewComponent>(Model))
    </div>
</div>

Demo

Here is Home controller of sample application. Index method deals with paged data. Code is simple and clean as you can see.

Click Run to run the demo

References

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content