Pagination In Blazor

In this article, we are going to add pagination to the table. In my previous article, I walked through CRUD Using Blazor, Entity Framework Core And Dapper and Sorting Table In Blazor.  If you haven’t read yet please read both articles first in order to understand this article. You can download the CRUD Blazor code from here. If you are new to Blazor, I recommend reading Getting Started With Blazor.

Prerequisite

  1. CRUD Using Blazor, Entity Framework Core And Dapper
  2. Sorting Table In Blazor

In the previous post, we have done with sorting logic. so we are continuing from there.

Let’s start!

Open “Articlemanager.cs” class and add Count() method.

public Task<int> Count()
{
   var totArticle = Task.FromResult(_dapperManager.Get<int>("select COUNT(*) from [Article]", null,
                    commandType: CommandType.Text));
   return totArticle;
}

Modify “ListAll()” method for pagination logic.In this method, we will pass four parameters.

  1. skip: How many records we want to skip.
  2. take: How many records we want to take from the database.
  3. orderBy: On which column you want to sort records. (necessary to use skip and take functionality.)
  4. direction: Order direction either “DESC” or “ASC”.

Our old SQL query was “select * from [Article]”, now we want to make some records and skip some records base on page click. To make this possible modify the select query as below.

select * from [Article] ORDER BY {orderBy} {direction} OFFSET {skip} ROWS FETCH NEXT {take} ROWS ONLY; ", null, commandType: CommandType.Text));
            return articles;

“ListAll()” Method will look like this after modification.

public Task<List<Article>> ListAll(int skip, int take, string orderBy, string direction = "DESC")
        {
            var articles = Task.FromResult(_dapperManager.GetAll<Article>
                ($"select * from [Article] ORDER BY {orderBy} {direction} OFFSET {skip} ROWS FETCH NEXT {take} ROWS ONLY; ", null, commandType: CommandType.Text));
            return articles;

        }

Open the “IArticleManager” interface and add the following line and update ListAll method().

Task<int> Count();

Task<List<Article>> ListAll(int skip, int take, string orderBy, string direction);

We have done with business logic, let modify the razor component.

Open the “FetchArticle.razor” page and replace the following code.

@page "/articlelist"

@using BlazorCRUD.Entities
@using BlazorCRUD.Contracts
@inject IArticleManager articleManager

<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

<style>
    .sort-th {
        cursor: pointer;
    }

    .fa {
        float: right;
    }

    .btn-custom {
        color: black;
        float: left;
        padding: 8px 16px;
        text-decoration: none;
        transition: background-color .3s;
        border: 2px solid #000;
        margin: 0px 5px 0px 5px;
    }
</style>

<div>
    <a class="btn btn-primary" href='/addArticle'>Add</a>
</div>
<br />

@if (articleModel == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table table-bordered table-hover">
        <thead>
            <tr>
                <th class="sort-th" @onclick="@(() => SortTable("ID"))">
                    ID
                    <span class="fa @(SetSortIcon("ID"))"></span>
                </th>
                <th class="sort-th" @onclick="@(() => SortTable("Title"))">
                    Title
                    <span class="fa @(SetSortIcon("Title"))"></span>
                </th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var article in articleModel)
            {
                <tr>
                    <td>@article.ID</td>
                    <td>@article.Title</td>
                    <td>
                        <a class="btn btn-primary" href='/editArticle/@article.ID'>Edit</a>  |
                        <a class="btn btn-danger" @onclick="() => DeleteArticle(article.ID)">Delete</a>
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <div class="pagination">
        <button class="btn btn-custom" @onclick=@(async ()=>await NavigateToPage("previous"))>Prev</button>

        @for (int i = startPage; i <= endPage; i++)
        {
            var currentPage = i;
            <button class="btn btn-custom pagebutton @(currentPage==curPage?"btn-danger":"")" @onclick=@(async () =>await refreshRecords(currentPage))>
                @currentPage
            </button>
        }

        <button class="btn btn-custom" @onclick=@(async ()=>await NavigateToPage("next"))>Next</button>

    </div>
}


@code {
    List<Article>
    articleModel;
    Article articleEntity = new Article();


    #region Pagination

    int totalPages;
    int totalRecords;
    int curPage;
    int pagerSize;
    int pageSize;
    int startPage;
    int endPage;
    string sortColumnName = "ID";
    string sortDir = "DESC";

    #endregion

    protected override async Task OnInitializedAsync()
    {
        //display total page buttons
        pagerSize = 3;
        pageSize = 2;
        curPage = 1;
        articleModel = await articleManager.ListAll((curPage - 1) * pageSize, pageSize, sortColumnName, sortDir);
        totalRecords = await articleManager.Count();
        totalPages = (int)Math.Ceiling(totalRecords / (decimal)pageSize);
        SetPagerSize("forward");
    }


    protected async Task DeleteArticle(int id)
    {
        await articleManager.Delete(id);
        articleModel = await articleManager.ListAll((curPage - 1) * pageSize, pageSize, sortColumnName, sortDir);
    }

    private bool isSortedAscending;
    private string activeSortColumn;

    private async Task<List<Article>> SortRecords(string columnName, string dir)
    {
        return await articleManager.ListAll((curPage - 1) * pageSize, pageSize, columnName, dir);
    }

    private async Task SortTable(string columnName)
    {
        if (columnName != activeSortColumn)
        {
            articleModel = await SortRecords(columnName, "ASC");
            isSortedAscending = true;
            activeSortColumn = columnName;
        }
        else
        {
            if (isSortedAscending)
            {
                articleModel = await SortRecords(columnName, "DESC");
            }
            else
            {
                articleModel = await SortRecords(columnName, "ASC");
            }

            isSortedAscending = !isSortedAscending;
        }
        sortColumnName = columnName;
        sortDir = isSortedAscending ? "ASC" : "DESC";
    }

    private string SetSortIcon(string columnName)
    {
        if (activeSortColumn != columnName)
        {
            return string.Empty;
        }
        if (isSortedAscending)
        {
            return "fa-sort-up";
        }
        else
        {
            return "fa-sort-down";
        }
    }

    public async Task refreshRecords(int currentPage)
    {
        articleModel = await articleManager.ListAll((currentPage - 1) * pageSize, pageSize, sortColumnName, sortDir);
        curPage = currentPage;
        this.StateHasChanged();
    }

    public void SetPagerSize(string direction)
    {
        if (direction == "forward" && endPage < totalPages)
        {
            startPage = endPage + 1;
            if (endPage + pagerSize < totalPages)
            {
                endPage = startPage + pagerSize - 1;
            }
            else
            {
                endPage = totalPages;
            }
            this.StateHasChanged();
        }
        else if (direction == "back" && startPage > 1)
        {
            endPage = startPage - 1;
            startPage = startPage - pagerSize;
        }
    }

    public async Task NavigateToPage(string direction)
    {
        if (direction == "next")
        {
            if (curPage < totalPages)
            {
                if (curPage == endPage)
                {
                    SetPagerSize("forward");
                }
                curPage += 1;
            }
        }
        else if (direction == "previous")
        {
            if (curPage > 1)
            {
                if (curPage == startPage)
                {
                    SetPagerSize("back");
                }
                curPage -= 1;
            }
        }
        await refreshRecords(curPage);
    }

}

Tada! we have done all the things.

It’s time to run application and you will get below output.

You can download the source code from here.

If you want to learn File Upload in Blazor please visit here.

Hiring professional developers from Vision Infotech could be the best choice if you’re searching for an enterprise-level .Net solution. We have a team of skilled .Net programmers on staff who create amazing solutions that meet the needs of our clients. We offer a robust infrastructure that can accommodate a wide range of corporate needs. Hire .Net developers in India from Vision Infotech and obtain high-quality solutions customized to your needs. We mix unique thought processes with the proper solutions to improve your clients’ user experience.

I hope you guys found something useful. Please give your valuable feedback/comments/questions about this article. Please let me know how you like and understand this article and how I could improve it.

5 Comments

  1. Haytham

    Hello,
    After Delete buttons numbers not refresh

    0
    0
    Reply
  2. Hairstyles VIP

    Hiya, I’m really glad I have found this information. Today bloggers publish just about gossips and web and this is actually frustrating. A good website with exciting content, this is what I need. Thanks for keeping this web site, I’ll be visiting it. Do you do newsletters? Can’t find it.

    0
    0
    Reply
    1. Faisal Pathan

      Thanks for the feedback, sure we will soon update newsletter.

      0
      0
      Reply
  3. Maurice

    THANKS FAISAL. YOUR ARTICLE HAS BEEN VERY HELPFUL ESPECIALLY AS YOU ARE USING SQL STORED PROCEDURES TO IMPLEMENT INSERT| AND UPDATE . PLS IN YOUR NEXT ARTICLE,

    PLS CAN YOU ADD FILTERING BY TYPING A FILTERING TEXT AND EXPORTING THE FILTERED PAGE TO MICROSOFT EXCEL.

    THANX FOR ALWAYS USING SQL SERVER STORED PROCEDURES.

    0
    0
    Reply
    1. Faisal Pathan

      Thanks for reading, Sure I’ll

      0
      0
      Reply

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories