cloudscribe.Web.Pagination

cloudscribe.Web.Pagination is an ASP.NET Core MVC TagHelper for paginated lists. We use it extensively in cloudscribe Core and cloudscribe SimpleContent, but it has no other "cloudscribe" dependecies itself, and you can use it in your projects even if you are not using other cloudscribe components. This pager was based heavily on the work of Martijn Boland in the older MVCPaging project.

Installation

Note that if you are already using cloudscribe Core or cloudscribe SimpleContent, you don't need to install cloudscribe.Web.Pagination separately, it is already included as a dependency. If you are not using either of those, you can install cloudscribe.Web.Pagination by editing your .csproj file, and adding a package reference like this:

<PackageReference Include="cloudscribe.Web.Pagination" Version="2.1.*" />

Also if you are not already using the "big" cloudscribe features then you need to add a line in the ConfigureServices method of your Startup.cs file like this:

services.AddCloudscribePagination();

and in your _ViewImports.cshtml file you need this:

@addTagHelper "*, cloudscribe.Web.Pagination"

That is all that is needed for the UI installation. However for convenience there is also available a generic PagedResult of T class that you can use in your data retrieval to return a list of any type along with the needed pagination info such as total items, page size, and current page. Naturally you would not want a dependency in your data storage layer to a UI component like the PagerTagHelper, so the PagedResult of T is actually in a separate nuget package named cloudscribe.Pagination.Models. You can add a package reference in your data storage project without brigning in any unwanted web stuff, like this:

<PackageReference Include="cloudscribe.Pagination.Models" Version="1.0.*" />

Some example code showing how to return a PagedResult of T using EntityFramework Core is shown below. This example returns a PagedResult of an EmailList class in one of my custom projects.

public async Task<PagedResult<EmailList>> GetLists(
	string projectId,
	int pageNumber,
	int pageSize,
	CancellationToken cancellationToken = default(CancellationToken)
	)
{
	cancellationToken.ThrowIfCancellationRequested();

	int offset = (pageSize * pageNumber) - pageSize;

	var query = db.EmailLists.OrderBy(x => x.Title)
		.Select(p => p)
		.Skip(offset)
		.Take(pageSize)
		;

	var result = new PagedResult<EmailList>();
	result.Data = await query.AsNoTracking().ToListAsync(cancellationToken);
	result.TotalItems = await db.EmailLists.CountAsync();
	result.PageNumber = pageNumber;
	result.PageSize = pageSize;

	return result;

}

Note that you must populate the total items, which requires a second query since the first query is using skip and take and therefore not getting all rows, so you can't just use the count of the list it returns. The above is just example code to give you an idea how you can use it in your own projects. In some cases you could even use the PagedResult of T as the viewmodel for your view, but often you will need other things in your viewmodel so you can just add the PagedResult of T as a property on your viewmodel and then also have other properties. Use of PagedResult of T makes it easier and more consistent to pass the needed info to the PagerTagHelper as shown below.

Using The PagerTagHelper

Now that everything is installed, you can use the PagerTagHelper in your views like this:

<cs-pager cs-paging-pagesize="@Model.PageSize"
  cs-paging-pagenumber="@Model.PageNumber"
  cs-paging-totalitems="@Model.TotalItemCount"
  cs-pagenumber-param="page"
  asp-controller="YourController"
  asp-action="YourAction"></cs-pager>

Note that you need to pass in the PageSize, current PageNumber, and TotalItems. The pager links will only be rendered if needed, ie if there exists more than one page of data as determined by the page size and total items. Note that the above example uses separate model properties for each of the needed parameters PageSize, PageNumber, and TotalItems. If you are using the PagedResult of T mentioned above as a property on your viewmodel you would use syntax like this:

<cs-pager asp-action="ListSubscribers"
  asp-controller="EmailList"
  asp-route-pagesize="@Model.Subscribers.PageSize"
  cs-paging-pagenumber="@Model.Subscribers.PageNumber"
  cs-paging-totalitems="@Model.Subscribers.TotalItems"
  asp-route-listId="@Model.EmailList.Id"
  asp-route-q="@Model.Q"
  asp-route-sortmode="@Model.SortMode"
  ></cs-pager>

In the above example there is a little more complexity in that we have several other route parameters, one for an email list id, one for a search term "Q", and one for a sort mode parameter. The Subscribers property of the view model is a PagedResult of EmailListSubscribers. For more clarity, the viewmodel used for the above sample looks like this:

using cloudscribe.Pagination.Models;

public class EmailSubscriberListViewModel
{
	public EmailSubscriberListViewModel()
	{
		Subscribers = new PagedResult<EmailListSubscription>();
	}

	public string Q { get; set; }

	public int SortMode { get; set; } = 0;

	public EmailList EmailList { get; set; }

	public PagedResult<EmailListSubscription> Subscribers { get; set; }
}

Additional PagerTagHelper attributes

The PagerTagHelper supports a number of configurable properties, shown below with their default values:

  • cs-paging-pagesize - 10

  • cs-paging-pagenumber - 1

  • cs-paging-totalitems - 1

  • cs-preserve-ambient-querystring - true by default, when true will preserve all query string parameters in the current url without having to explicitly add asp-route-[parametername]. Set to false if you don't want this behavior.

  • cs-paging-maxpageritems - 10

  • cs-pagenumber-param - pageNumber

  • cs-show-first-last - false

  • cs-first-page-text - <

  • cs-first-page-title - First Page

  • cs-last-page-text - >

  • cs-last-page-title - Last Page

  • cs-previous-page-text - «

  • cs-previous-page-title - Previous page

  • cs-next-page-text - »

  • cs-next-page-title - Next page

  • cs-previous-page-html - pass in raw html as shown in the example above, you must provide the title and your html must have a link with href='#'

  • cs-next-page-html - same as above

  • cs-pager-ul-class - pagination

  • cs-pager-li-current-class - active

  • cs-pager-li-non-active-class - disabled

  • cs-ajax-target - empty - should be the id of the html element to target for ajax updates

  • cs-ajax-mode - replace

  • cs-ajax-success - empty - a callback

  • cs-ajax-failure - empty - a callback

  • cs-ajax-loading - empty - the #id of an image ie animated gif to show while loading

  • cs-ajax-loading-duration - empty - corresponds to data-ajax-loading-duration

For the next page and previous page you can optionally pass in html fragments. An example below shows how to use glyphicons for the next and previous pager links:

<cs-pager cs-paging-pagesize="@Model.PageSize"
  cs-paging-pagenumber="@Model.PageNumber"
  cs-paging-totalitems="@Model.TotalItemCount"
  cs-pagenumber-param="page"
  cs-previous-page-html="<a href='#' title='Previous page'><i class='glyphicon glyphicon-backward'></i></a>"
  cs-next-page-html="<a href='#' title='Next page'><i class='glyphicon glyphicon-forward'></i></a>"
  asp-controller="YourController"
  asp-action="YourAction"></cs-pager>

Note that if you pass in html, your html must provide the title and an a element with the href='#', the taghelper will replace the # with the correct url as needed.

To use ajax, you must include jquery.unobtrusive-ajax.js in the page.

Bootstrap 4

Bootstrap 4 uses different CSS classes than bootstrap 3, the default CSS classes used are the bootstrap 3 versions but you can change the css classes to use the ones for bootstrap 4 like this:

cs-pager-li-current-class="page-item active"
cs-pager-li-other-class="page-item"
cs-pager-li-non-active-class="page-item disabled"
cs-pager-link-current-class="page-link"
cs-pager-link-other-class="page-link"

Alphabetic Paginatation

In addition to the PagerTagHelper, there is also an AlphaPagerTagHelper, that can be used for filtering paged lists by some property of the list items. We use this in cloudscribe Core on the User List to filter by the first letter of a user's name. We use it in combination with the PagerTagHelper.

Example use from our demo app looks like this:

<cs-alphapager cs-alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    cs-selected-letter="@Model.Query"
    cs-all-label="All"
    asp-controller="Paging"
    asp-action="ProductList" 
    cs-selected-letter-param="query"
    ></cs-alphapager>

The supported attributes with their default values are:

cs-alphabet - ABCDEFGHIJKLMNOPQRSTUVWXYZ

cs-populated-letters - null - you can pass in an IEnumerable indicating which letters actually have data so that ones without data can be non links. If not provided then every letter will be a link

  • cs-selected-letter - empty string
  • cs-selected-letter-param = "letter"
  • cs-all-label - "All"
  • cs-all-value - empty string
  • cs-include-numbers - false
  • cs-alphapager-ul-class - "pagination alpha"
  • cs-pager-li-current-class - "active"
  • cs-pager-li-non-active-class = "inactive"

For Bootstrap 4 you probably need to set the CSS class names like this:

cs-pager-li-current-class="page-item active"
cs-pager-li-non-active-class="page-item"

Demos

In our github repository, we have a demo web application that illustrates most of the available features including some ajax examples that show a loading indicator and show how to use the callbacks. In the demo the callbacks just log to the console in the web browser.

Comments