Advertisement

#29 Generating The Offset Pagination Urls

In this article we will focus on generating all of the information that we need to display all of the various components of our pager to the user. We will start by generating the stats which will tell the user which items they are viewing out of how many, next we will generate the urls for the first, next, previous and last anchors, then we will generate the information for the individual page anchors using a separate anchor class and lastly we will determine which of the first, next, previous, and last anchors should be shown at all.

Generate the stats

  • WebUi
    • Features
      • Paging
        • OffsetPager.cs

We are going to approach creating the information that we need for our pager step by step. The first step is to generate what we are going to call the stats (a). The stats are what we are going to use to tell the user what entities they are viewing out of the total available.

OffsetPager.cs

...
namespace WebUi.Features.Paging
{
    public class OffsetPager
    {
        ...
        public long StatFirst { get; private set; }
        public long StatLast { get; private set; }
        ...
        public async Task<(bool redirect, RedirectToPageResult result, List<T> entities)> CreateAsync<T>(
            int page, IQueryable<T> queryable)
        {
            ...
            var entities = await GetPageEntitiesAsync(page, queryable);

            GenerateUI(page);
            ...
        }

        public void GenerateUI(int page)
        {
            GenerateStats(page);
        }
        ...
        private void GenerateStats(int page)
        {
            StatFirst = (page - 1) * Take + 1;
            StatLast = page == LastPage
                ? TotalCount
                : page * Take;
        }
    }
}
(a) First up were are going to determine what entities the user is viewing out of the total available.

View the stats

  • WebUi
    • Pages
      • Offset-Pager.cshtml

Just to make sure everything is working we will make a quick update to our page to see if our stats are working correctly (b).

Offset-Pager.cshtml

...
<div>@Model.Pager.StatFirst - @Model.Pager.StatLast of @Model.Pager.TotalCount</div>
(b) Updating our page so that we can see how are stats are doing.

Generate First, Previous, Next and Last

  • WebUi
    • Features
      • Paging
        • OffsetPager.cs

Next up we are going to generate the urls that we need to allow the user to go to the first, next, previous and last pages (c). We are also going to allow for the possibility that we may need to have additional route values added to our urls.

OffsetPager.cs

...
using Microsoft.AspNetCore.Routing;
...
namespace WebUi.Features.Paging
{
    public class OffsetPager
    {
        private const string PageParamName = "pg";
        ...
        public string FirstUrl { get; private set; }
        ...
        public string LastUrl { get; private set; }
        public string NextUrl { get; private set; }
        public string PrevUrl { get; private set; }
        ...
        public async Task<(bool redirect, RedirectToPageResult result, List<T> entities)> CreateAsync<T>(
            ..., object routeValues = null)
        {
            ...
            GenerateUI(page, routeValues);
            ...
        }

        public void GenerateUI(..., object routeValues = null)
        {
            var rtValues = routeValues == null
                ? new RouteValueDictionary()
                : new RouteValueDictionary(routeValues);

            if (rtValues.ContainsKey(PageParamName) == false)
            {
                rtValues.Add(PageParamName, 1);
            }
            ...
            string Url(RouteValueDictionary x) => _model.Url.Page(_pageName, x);
            GenerateFirstNextPrevLast(page, rtValues, Url);
        }
        ...
        private void GenerateFirstNextPrevLast(int page, RouteValueDictionary routeValues,
            Func<RouteValueDictionary, string> url)
        {
            routeValues[PageParamName] = 1;
            FirstUrl = url(routeValues);

            routeValues[PageParamName] = page - 1;
            PrevUrl = url(routeValues);

            routeValues[PageParamName] = page + 1;
            NextUrl = url(routeValues);

            routeValues[PageParamName] = LastPage;
            LastUrl = url(routeValues);
        }
        ...
    }
}
(c) Code to generate the first, previous, next, and last urls as well as allow for the possibility that there needs to be other route values added to the urls.

View First, Previous, Next and Last

  • WebUi
    • Pages
      • Offset-Pager.cshtml

Again we now update our page so that we can see what new urls we are generating (d).

Offset-Pager.cshtml

...
<div>First @Model.Pager.FirstUrl</div>
<div>Prev @Model.Pager.PrevUrl</div>
<div>Next @Model.Pager.NextUrl</div>
<div>Last @Model.Pager.LastUrl</div>
(d) Adding the new urls to our page so that we can make sure they are being generated correctly.
Advertisement

The individual page anchors

  • WebUi
    • Features
      • Paging
        • OffsetPagerAnchor.cs

For the anchor tags that will allow a user to jump to a particular page we will need a bit more information and to allow for this we will create a new anchor class (e).

OffsetPagerAnchor.cs

namespace WebUi.Features.Paging
{
    public class OffsetPagerAnchor
    {
        public OffsetPagerAnchor(string text, string url)
        {
            CssClass = "";
            Text = text;
            Url = url;
        }

        public string CssClass { get; set; }
        public bool IsTextOnly { get; set; }
        public string Text { get; }
        public string Url { get; }
    }
}
(e) We will use the anchor class to provide the information to our page that we will need to create and style our anchor tags.

Generating the page anchors

  • WebUi
    • Features
      • Paging
        • OffsetPager.cs

Now that we have a anchor class to hold the information it is now time to generate the anchors that we need for our page (f).

OffsetPager.cs

...
namespace WebUi.Features.Paging
{
    public class OffsetPager
    {
        private const string ActiveAnchorCssClass = "active";
        ...
        public OffsetPager()
        {
            ...
            Anchors         = new List<OffsetPagerAnchor>();
            NumberOfAnchors = 5;
            ...
        }

        public List<OffsetPagerAnchor> Anchors { get; }
        ...
        public int NumberOfAnchors { get; set; }
        ...
        public void GenerateUI(int page, object routeValues = null)
        {
            ...
            GeneratePages(page, rtValues, Url);
        }
        ...
        private void GeneratePages(int page, RouteValueDictionary routeValues,
            Func<RouteValueDictionary, string> url)
        {
            var middle = (int)Math.Ceiling(NumberOfAnchors / 2.0);
            int start;
            int end;

            if (page <= middle)
            {
                start = 1;
                end = Math.Min(NumberOfAnchors, LastPage);
            }
            else if (LastPage - page < middle)
            {
                start = LastPage - NumberOfAnchors + 1;
                end = LastPage;
            }
            else
            {
                start = page - middle + 1;
                end = page + middle - 1;
            }

            for (var i = start; i <= end; i++)
            {
                routeValues[PageParamName] = i;
                var anchor = new OffsetPagerAnchor($"{i}", url(routeValues));
                if (i == page)
                {
                    anchor.CssClass = ActiveAnchorCssClass;
                    anchor.IsTextOnly = true;
                }
                Anchors.Add(anchor);
            }
        }
        ...
    }
}
(f) The code needed to generate the data that we will use to create individual anchors tags that we will display on our page.

Viewing the individual page anchors

  • WebUi
    • Pages
      • Offset-Paging.cshtml

Sticking with tradition we will now make sure that we are generating the correct data for our individual pages by simply iterating over our anchors and displaying their urls (g).

Offset-Paging.cshtml

...
@foreach (var anchor in Model.Pager.Anchors)
{
    <div>Page @anchor.Url</div>
}
(g) Displaying the url's of our individual pages.

What should we show the user?

  • WebUi
    • Features
      • Paging
        • OffsetPager.cs

Last up is adding a way for us to decide what if any of the available anchor tags should be shown to the user (h). Strictly speaking we would only need one value for the pairs ShowFirst, ShowPrev and ShowNext, ShowLast but for clarity in the page we are opting to have one value for each.

OffsetPager.cs

...
namespace WebUi.Features.Paging
{
    public class OffsetPager
    {
        public OffsetPager(PageModel model, string pageName)
        {
            ...
            Show      = true;
            ShowLast  = true;
            ShowNext  = true;
            ShowFirst = true;
            ShowPrev  = true;
            ...
        }
        ...
        public bool Show { get; private set; }
        public bool ShowFirst { get; private set; }
        public bool ShowLast { get; private set; }
        public bool ShowNext { get; private set; }
        public bool ShowPrev { get; private set; }
        ...

        public void GenerateUI(int page)
        {
            ...
            GenerateShow(page);
        }
        ...
        private void GenerateShow(int page)
        {
            if (Take >= TotalCount)
            {
                Show = false;
                return;
            }

            if (page == 1)
            {
                ShowFirst = false;
                ShowPrev = false;
            }
            else if (page == LastPage)
            {
                ShowNext = false;
                ShowLast = false;
            }
        }
        ...
    }
}
(h) The last thing to add to our pager is the ability to show or not show the first, previous, next and last anchors (i).

To show or not to show

  • WebUi
    • Pages
      • OffsetPager.cshtml

Finally we will update our page so that the appropriate urls are shown depending on what the current page is.

OffsetPager.cshtml

...
@if (Model.Pager.ShowFirst)
{
    <div>First @Model.Pager.FirstUrl</div>
}
@if (Model.Pager.ShowPrev)
{
    <div>Prev @Model.Pager.PrevUrl</div>
}
@if (Model.Pager.ShowNext)
{
    <div>Next @Model.Pager.NextUrl</div>
}
@if (Model.Pager.ShowLast)
{
    <div>Last @Model.Pager.LastUrl</div>
}
...
(i) Conditionally showing anchors based on the current page.
Exciton Interactive LLC
Advertisement