liveBook · Manning

Pagination

As explained in Pro Angular 16

Pagination is a technique for dividing a collection of results into separate pages or subsets for presentation or retrieval.

Pagination divides a collection into manageable pages for presentation or retrieval. A product store can let users choose the page size and navigate numbered pages instead of viewing every product at once. section 5.5.3

A custom Angular implementation tracks and derives the page state:

  • Store the products-per-page and selected-page values as signals. section 5.5.3
  • Compute the available page numbers from the product count and page size. section 5.5.3
  • Bind a page-size select and navigation buttons to component properties and methods. section 5.5.3
  • Parse values received from HTML controls because statically defined options provide strings at runtime. section 5.5.3

For an administration table, Angular Material supplies a paginator. Add MatPaginatorModule, place the paginator alongside the table, and associate it with the MatTableDataSource after the view initializes. section 7.5.3

Product table pagination

A product table displays columns for identifiers, names, categories, prices, and actions. Connecting its data source to MatPaginator adds built-in pagination while preserving the table component’s existing structure. section 7.5.3

Listing 5.23. Adding pagination in the store.component.ts file in the src/app/store folder
import { Component, Signal, computed, signal } from "@angular/core";
import { Product } from "../model/product.model";
import { ProductRepository } from "../model/product.repository";
 
@Component({
    selector: "store",
    templateUrl: "store.component.html"
})
export class StoreComponent {
    products: Signal<Product[]>;
    categories: Signal<string[]>;
    selectedCategory = signal<string | undefined>(undefined);
    productsPerPage = signal(4);
    selectedPage = signal(1);
    pagedProducts: Signal<Product[]>;
    pageNumbers: Signal<number[]>;

    constructor(private repository: ProductRepository) { 
        this.products = computed(() => {
            if (this.selectedCategory() == undefined) {
                return this.repository.products();
            } else {
                return this.repository.products().filter(p => 
                    p.category === this.selectedCategory());
            }
        })
        this.categories = repository.categories;
        let pageIndex = computed(() => {
            return (this.selectedPage() - 1) * this.productsPerPage()
        });        
        this.pagedProducts = computed(() => {
            return this.products().slice(pageIndex(), 
                pageIndex() + this.productsPerPage());
        });
        this.pageNumbers = computed(() => {
            return Array(Math.ceil(this.products().length 
                    / this.productsPerPage()))
                .fill(0).map((x, i) => i + 1);            
        });
    }

    changeCategory(newCategory?: string) {
        this.selectedCategory.set(newCategory);
        this.changePage(1);        
    }

    changePage(newPage: number) {
        this.selectedPage.set(newPage);
    }

    changePageSize(newSize: number) {
        this.productsPerPage.set(Number(newSize));
        this.changePage(1);
    }
}

Inspect the custom StoreComponent implementation, including signal state and computed page-number generation. section 5.5.3

Listing 7.34. Connecting the paginator in the productTable.component.ts file in the src/app/admin folder
import { Component, IterableDiffer, IterableDiffers, ViewChild } 
    from "@angular/core";
import { MatTableDataSource } from "@angular/material/table";
import { Product } from "../model/product.model";
import { ProductRepository } from "../model/product.repository";
import { MatPaginator } from "@angular/material/paginator";

@Component({
    templateUrl: "productTable.component.html"
})
export class ProductTableComponent {
    colsAndRows: string[] = ['id', 'name', 'category', 'price', 'buttons'];
    dataSource =
        new MatTableDataSource<Product>(this.repository.products());
    differ: IterableDiffer<Product>;

    constructor(private repository: ProductRepository, 
            differs: IterableDiffers) { 
        this.differ = differs.find(this.repository.products()).create();
    }

    ngDoCheck() {
        let changes = this.differ?.diff(this.repository.products());
        if (changes != null) {
            this.dataSource.data = this.repository.products();
        }        
    }

    deleteProduct(id: number) {
        this.repository.deleteProduct(id);
    }    

    @ViewChild(MatPaginator)
    paginator? : MatPaginator

    ngAfterViewInit() {
        if (this.paginator) {
            this.dataSource.paginator = this.paginator;
        }
    }    
}

Inspect the Angular Material integration that queries MatPaginator and connects it to the table data source. section 7.5.3

How Pro Angular 16 covers it

How deep it goes
Demonstrates custom state, computed page numbers, template bindings, module setup, and lifecycle-based data-source wiring.
Examples
Uses a SportsStore product grid and an administration product table as concrete pagination examples.
What it assumes
Assumes Angular components and templates, while refreshing runtime bindings, signals, data sources, ViewChild, and lifecycle timing.
What you can do after
Apply it via Angular signals or Angular Material to build navigable product grids and paginated data tables.
How it connects
Connects pagination with Angular signals, ngFor, Angular Material tables, MatTableDataSource, MatPaginator, ViewChild, and lifecycle hooks.

Also explained in

Compare how 11 books explain pagination →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro Angular 16. The section links point to the parts of the book it draws on.