PagedList - spinningideas/resources GitHub Wiki
The JavaScript/TypeScript implementation mirrors the C# PagedList<T> pattern. It uses the Pagination class and ApiResponsePaged<T> types defined in Paging-Data.
export class Pagination {
public totalResults: number = 0;
public totalPages: number = 0;
public pageSize: number = 20;
public pageNumber: number = 1;
public resultsExist: boolean = false;
public hasPreviousPage: boolean = false;
public hasNextPage: boolean = false;
constructor(pageSize: number, pageNumber: number, total: number) {
this.pageSize = pageSize;
this.pageNumber = pageNumber;
this.totalResults = total;
this.resultsExist = total > 0;
this.totalPages = Math.ceil(this.totalResults / this.pageSize);
this.hasPreviousPage = pageNumber > 1;
this.hasNextPage =
this.pageSize === 1
? this.totalResults > this.pageSize
: this.totalPages > pageNumber;
}
}import { Pagination } from "./pagination";
import type ApiResponsePaged from "./types";
export class PagedList<T> {
public items: T[];
public pagination: Pagination;
constructor(items: T[], pageSize: number, pageNumber: number, total: number) {
this.items = items;
this.pagination = new Pagination(pageSize, pageNumber, total);
}
get currentPage(): number {
return this.pagination.pageNumber;
}
get totalPages(): number {
return this.pagination.totalPages;
}
get totalResults(): number {
return this.pagination.totalResults;
}
get hasNextPage(): boolean {
return this.pagination.hasNextPage;
}
get hasPreviousPage(): boolean {
return this.pagination.hasPreviousPage;
}
/** Build a PagedList directly from a typed API response */
static fromResponse<T>(response: ApiResponsePaged<T[]>): PagedList<T> {
const p = response.pagination;
if (!p) {
throw new Error("Response does not contain pagination metadata");
}
return new PagedList<T>(
response.data ?? [],
p.pageSize,
p.pageNumber,
p.totalResults,
);
}
/** Slice an in-memory array into a PagedList (equivalent to C# ToPagedList) */
static fromArray<T>(
source: T[],
pageNumber: number,
pageSize: number,
): PagedList<T> {
const total = source.length;
const items = source.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
return new PagedList<T>(items, pageSize, pageNumber, total);
}
}import { PagedList } from "./pagedList";
// --- From an API response ---
const response = await fetch("/api/products/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ searchTerm: "widget", pageNumber: 1, pageSize: 10 }),
}).then((r) => r.json());
const page = PagedList.fromResponse<Product>(response);
console.log(page.items); // Product[] for the current page
console.log(page.currentPage); // 1
console.log(page.totalPages); // 5
console.log(page.hasNextPage); // true
console.log(page.hasPreviousPage); // false
// --- From an in-memory array ---
const all = [/* ...47 products... */];
const page2 = PagedList.fromArray(all, 2, 10);
console.log(page2.items.length); // 10
console.log(page2.currentPage); // 2
console.log(page2.hasNextPage); // truepublic class PagedList<T> : List<T>
{
public int CurrentPage { get; private set; }
public int TotalPages { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public bool HasPrevious => CurrentPage > 1;
public bool HasNext => CurrentPage < TotalPages;
public PagedList(List<T> items, int count, int pageNumber, int pageSize)
{
TotalCount = count;
PageSize = pageSize;
CurrentPage = pageNumber;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
AddRange(items);
}
public static PagedList<T> ToPagedList(IQueryable<T> source, int pageNumber, int pageSize)
{
var count = source.Count();
var items = source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
}