#29 Generating The Offset Pagination Urls
Saturday, May 4, 2019
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.
Parts
- Part 29: Offset Pager Urls
- Part 28: Offset Pager Start
- Part 27: Mock Context Builder
- Part 26: Mock Repository
- Part 25: Mock Async
- Part 24: Picture Tag Helper
- Part 23: Img DPR Tag Helper
- Part 22: Img Responsive Tag Helper
- Part 21: Img Optimized Display
- Part 20: Img Optimization
- Part 19: Img Lazy Loading
- Part 18: Img Responsive
- Part 17: Bottom Nav
- Part 16: Main Nav Cookie
- Part 15: Main Nav Mobile
- Part 14: Main Nav Search
- Part 13: Main Nav Auth
- Part 12: Main Nav Anchors
- Part 11: Main Nav Logo
- Part 10: Search Results
- Part 9: Search Manager
- Part 8: Search Start
- Part 7: Seeding the Database
- Part 6: Domain Database
- Part 5: Emailing Exceptions
- Part 4: Mailkit
- Part 3: View Renderer
- Part 2: Upgrade to 2.1
- Part 1: Quick Start
Generate the stats
- WebUi
- Features
- Paging
- OffsetPager.cs
- Paging
- Features
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;
}
}
}
View the stats
- WebUi
- Pages
- Offset-Pager.cshtml
- Pages
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>
Generate First, Previous, Next and Last
- WebUi
- Features
- Paging
- OffsetPager.cs
- Paging
- Features
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);
}
...
}
}
View First, Previous, Next and Last
- WebUi
- Pages
- Offset-Pager.cshtml
- Pages
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>
The individual page anchors
- WebUi
- Features
- Paging
- OffsetPagerAnchor.cs
- Paging
- Features
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; }
}
}
Generating the page anchors
- WebUi
- Features
- Paging
- OffsetPager.cs
- Paging
- Features
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);
}
}
...
}
}
Viewing the individual page anchors
- WebUi
- Pages
- Offset-Paging.cshtml
- Pages
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>
}
What should we show the user?
- WebUi
- Features
- Paging
- OffsetPager.cs
- Paging
- Features
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;
}
}
...
}
}
To show or not to show
- WebUi
- Pages
- OffsetPager.cshtml
- Pages
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>
}
...