Primitives
Pagination
A navigation landmark that derives a visible page list with ellipsis gaps from page, count, siblingCount and boundaryCount, with previous / next buttons and aria-current='page' on the active page.
Walk the pages with the pointer or the keyboard. The current page is aria-current="page", and the ends reflect data-disabled on the arrow that has nowhere to go.
Anatomy
<nav forPagination [(page)]="page" [count]="20" ariaLabel="Pagination" #pg="forPagination">
<button forPaginationPrevious ariaLabel="Previous page">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path
d="m15.75 19.5-7.5-7.5 7.5-7.5"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<!-- one item per entry in pg.items() — a page button per 'page' entry -->
<button forPaginationItem [page]="item.value!">{{ item.value }}</button>
<!-- an aria-hidden gap per 'ellipsis' entry -->
<span aria-hidden="true">…</span>
<button forPaginationNext ariaLabel="Next page">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path
d="m8.25 4.5 7.5 7.5-7.5 7.5"
fill="none"
stroke="currentColor"
stroke-width="1.75"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</nav>
Examples
Driving a data list
Pagination is headless state: derive count from your data, then slice the visible rows from page(). Changing the page re-slices the list; the page model is the single source of truth shared by the rows and the controls.
- INV-1001Acme$120.00
- INV-1002Globex$157.00
- INV-1003Initech$194.00
- INV-1004Umbrella$231.00
- INV-1005Soylent$268.00
1–5 of 23 invoices
API
ForPagination
| Property | Type | Description |
|---|---|---|
page | model | Two-way bindable. The currently active page number. Default: — |
count | input | Total number of pages. Default: — |
siblingCount | input | Number of page buttons to show on each side of the current page. Default: — |
boundaryCount | input | Number of page buttons to always show at the start and end of the list. Default: — |
ariaLabel | input | Accessible label for the navigation landmark.Default: — |
| Data attribute | Values | |
|---|---|---|
data-disabled | present (no value) when disabled is set |
ForPaginationItem
| Property | Type | Description |
|---|---|---|
page | input | The page number this button represents. Default: — |
ForPaginationPrevious
| Property | Type | Description |
|---|---|---|
ariaLabel | input | Accessible label for the previous-page button. Default: — |
ForPaginationNext
| Property | Type | Description |
|---|---|---|
ariaLabel | input | Accessible label for the next-page button. Default: — |
Accessibility
Implements the WAI-ARIA navigation landmark pattern.
- Navigation landmark.
[forPagination]appliesrole="navigation". SetariaLabel(e.g."Pagination") so the landmark is distinguishable from other navigation regions on the page. - Current page.
[forPaginationItem]reflectsaria-current="page"on the active page button so screen-reader users know which page they are on. - Disabled prev/next.
[forPaginationPrevious]and[forPaginationNext]use the nativedisabledattribute at the boundaries, which suppresses click and removes the element from the tab order.
Styling
forty-cdk ships no styles. Style the current page via [aria-current="page"] and disabled prev/next via :disabled.
[forPaginationItem][aria-current='page'] {
font-weight: bold;
}
[forPaginationPrevious]:disabled,
[forPaginationNext]:disabled {
opacity: 0.4;
pointer-events: none;
}
Wrapping in a design system
Subclass the root and re-provide FOR_PAGINATION_CONTEXT with useExisting pointing at the subclass, since Angular does not inherit a directive's providers; Wrapping non-form roots walks the pattern.