Skip to content

Commit dab8e92

Browse files
authored
Merge pull request #1930 from wheels-dev/peter/pagination-helpers-1915
Add composable pagination view helpers
2 parents 489cfbe + 9d61204 commit dab8e92

File tree

7 files changed

+1074
-0
lines changed

7 files changed

+1074
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Pagination View Helpers
2+
3+
Composable helpers for building custom pagination UIs. All read from pagination metadata set by `findAll(page=...)` or `setPagination()`.
4+
5+
## Helpers
6+
7+
### paginationInfo(handle, format, encode)
8+
Text summary. Default format: `"Showing [startRow]-[endRow] of [totalRecords] records"`.
9+
Tokens: `[startRow]`, `[endRow]`, `[totalRecords]`, `[currentPage]`, `[totalPages]`.
10+
Returns `"No records found"` when totalRecords is 0.
11+
12+
### previousPageLink(text, handle, name, class, disabledClass, showDisabled, pageNumberAsParam, encode)
13+
Link to previous page. Renders `<span class="disabled">` on first page (or empty string if `showDisabled=false`).
14+
15+
### nextPageLink(text, handle, name, class, disabledClass, showDisabled, pageNumberAsParam, encode)
16+
Link to next page. Renders disabled span on last page.
17+
18+
### firstPageLink(text, handle, name, class, disabledClass, showDisabled, pageNumberAsParam, encode)
19+
Link to first page. Disabled when already on page 1.
20+
21+
### lastPageLink(text, handle, name, class, disabledClass, showDisabled, pageNumberAsParam, encode)
22+
Link to last page. Disabled when already on last page.
23+
24+
### pageNumberLinks(windowSize, handle, name, class, classForCurrent, linkToCurrentPage, prependToPage, appendToPage, pageNumberAsParam, encode)
25+
Windowed page numbers. Current page renders as `<span class="current">` unless `linkToCurrentPage=true`.
26+
27+
### paginationNav(handle, navClass, showFirst, showLast, showPrevious, showNext, showInfo, showSinglePage, encode)
28+
Complete `<nav class="pagination">` composing all above helpers. Returns empty string for single page unless `showSinglePage=true`.
29+
30+
## Defaults (config/settings.cfm)
31+
```cfm
32+
set(functionName="previousPageLink", text="&laquo; Prev");
33+
set(functionName="nextPageLink", text="Next &raquo;");
34+
set(functionName="paginationInfo", format="Page [currentPage] of [totalPages]");
35+
set(functionName="pageNumberLinks", windowSize=5);
36+
```
37+
38+
## Multiple Handles
39+
```cfm
40+
// Controller
41+
users = model("User").findAll(page=params.userPage, perPage=10, handle="users", order="name");
42+
// View
43+
#paginationNav(handle="users")#
44+
```
45+
46+
## Relationship to paginationLinks()
47+
These helpers complement (not replace) the existing `paginationLinks()` monolithic helper. Use `paginationLinks()` for quick defaults; use these composable helpers when you need custom layouts.
48+
49+
## Implementation
50+
- File: `vendor/wheels/view/pagination.cfc`
51+
- Defaults: `vendor/wheels/events/init/functions.cfm`
52+
- Tests: `tests/specs/view/PaginationHelpersSpec.cfc`
53+
- All helpers use `$args()` for defaults, `pagination()` for metadata, and `linkTo()` for link generation.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ All historical references to "CFWheels" in this changelog have been preserved fo
2121
## [Unreleased]
2222

2323
### Added
24+
- Composable pagination view helpers: `paginationInfo()`, `previousPageLink()`, `nextPageLink()`, `firstPageLink()`, `lastPageLink()`, `pageNumberLinks()`, and `paginationNav()` for building custom pagination UIs
2425
- Route model binding with `binding=true` on resource routes or `set(routeModelBinding=true)` globally to auto-resolve model instances from route key parameters
2526
- Chainable query builder with `where()`, `orWhere()`, `whereNull()`, `whereBetween()`, `whereIn()`, `orderBy()`, `limit()`, and more for injection-safe fluent queries
2627
- Enum support with `enum()` for named property values, auto-generated `is*()` checkers, auto-scopes, and inclusion validation

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
* [Layouts](displaying-views-to-users/layouts.md)
158158
* [Form Helpers and Showing Errors](displaying-views-to-users/form-helpers-and-showing-errors.md)
159159
* [Displaying Links for Pagination](displaying-views-to-users/displaying-links-for-pagination.md)
160+
* [Pagination Helpers](displaying-views-to-users/pagination-helpers.md)
160161
* [Date, Media, and Text Helpers](displaying-views-to-users/date-media-and-text-helpers.md)
161162
* [Creating Custom View Helpers](displaying-views-to-users/creating-custom-view-helpers.md)
162163
* [Localization](displaying-views-to-users/localization.md)
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# Pagination Helpers
2+
3+
Wheels provides composable pagination helpers that give you fine-grained control over pagination UI. Unlike `paginationLinks()` which generates a complete pagination block, these helpers let you build custom layouts by combining individual components.
4+
5+
All helpers read from the same pagination metadata set by `findAll(page=...)` or `setPagination()`.
6+
7+
## Quick Start
8+
9+
```cfm
10+
<!--- Controller --->
11+
<cfscript>
12+
users = model("User").findAll(page=params.page, perPage=25, order="lastName");
13+
</cfscript>
14+
15+
<!--- View --->
16+
#paginationNav()#
17+
```
18+
19+
This renders a complete `<nav>` element with first/previous/page numbers/next/last links.
20+
21+
## Individual Helpers
22+
23+
### paginationInfo
24+
25+
Displays a text summary of the current pagination state.
26+
27+
```cfm
28+
#paginationInfo()#
29+
<!--- Output: "Showing 26-50 of 1,000 records" --->
30+
31+
#paginationInfo(format="Page [currentPage] of [totalPages]")#
32+
<!--- Output: "Page 2 of 40" --->
33+
```
34+
35+
Available tokens: `[startRow]`, `[endRow]`, `[totalRecords]`, `[currentPage]`, `[totalPages]`.
36+
37+
### previousPageLink / nextPageLink
38+
39+
```cfm
40+
#previousPageLink()# <!--- "Previous" link or disabled span --->
41+
#nextPageLink()# <!--- "Next" link or disabled span --->
42+
43+
<!--- Custom text --->
44+
#previousPageLink(text="&laquo;")#
45+
#nextPageLink(text="&raquo;")#
46+
47+
<!--- Hide when disabled instead of showing span --->
48+
#previousPageLink(showDisabled=false)#
49+
```
50+
51+
### firstPageLink / lastPageLink
52+
53+
```cfm
54+
#firstPageLink()# <!--- "First" link or disabled span --->
55+
#lastPageLink()# <!--- "Last" link or disabled span --->
56+
```
57+
58+
### pageNumberLinks
59+
60+
Renders a windowed set of page number links around the current page.
61+
62+
```cfm
63+
#pageNumberLinks()#
64+
<!--- Output: 1 2 <span class="current">3</span> 4 5 --->
65+
66+
<!--- Larger window --->
67+
#pageNumberLinks(windowSize=5)#
68+
69+
<!--- With list markup --->
70+
#pageNumberLinks(prependToPage="<li>", appendToPage="</li>")#
71+
```
72+
73+
### paginationNav
74+
75+
Composes all helpers into a semantic `<nav>` element.
76+
77+
```cfm
78+
<!--- Full navigation --->
79+
#paginationNav()#
80+
81+
<!--- Without first/last links --->
82+
#paginationNav(showFirst=false, showLast=false)#
83+
84+
<!--- With info text --->
85+
#paginationNav(showInfo=true)#
86+
87+
<!--- Custom CSS class --->
88+
#paginationNav(navClass="my-pagination")#
89+
```
90+
91+
## Building a Custom Pagination UI
92+
93+
The real power comes from combining helpers individually:
94+
95+
```cfm
96+
<nav class="pagination" aria-label="Pagination">
97+
<div class="pagination-info">
98+
#paginationInfo()#
99+
</div>
100+
<ul class="pagination-links">
101+
<li>#previousPageLink(text="&laquo; Prev", disabledClass="text-muted")#</li>
102+
#pageNumberLinks(
103+
prependToPage="<li>",
104+
appendToPage="</li>",
105+
classForCurrent="active"
106+
)#
107+
<li>#nextPageLink(text="Next &raquo;", disabledClass="text-muted")#</li>
108+
</ul>
109+
</nav>
110+
```
111+
112+
## Bootstrap 5 Example
113+
114+
```cfm
115+
<nav aria-label="Page navigation">
116+
<ul class="pagination">
117+
<li class="page-item">#previousPageLink(text="&laquo;", class="page-link", disabledClass="page-link")#</li>
118+
#pageNumberLinks(
119+
prependToPage="<li class='page-item'>",
120+
appendToPage="</li>",
121+
class="page-link",
122+
classForCurrent="page-link active"
123+
)#
124+
<li class="page-item">#nextPageLink(text="&raquo;", class="page-link", disabledClass="page-link")#</li>
125+
</ul>
126+
</nav>
127+
```
128+
129+
## Common Parameters
130+
131+
All helpers support these parameters:
132+
133+
| Parameter | Default | Description |
134+
|-----------|---------|-------------|
135+
| `handle` | `"query"` | Handle for the paginated query |
136+
| `encode` | `true` | HTML-encode output |
137+
138+
Link helpers (`previousPageLink`, `nextPageLink`, `firstPageLink`, `lastPageLink`) also support:
139+
140+
| Parameter | Default | Description |
141+
|-----------|---------|-------------|
142+
| `text` | Varies | Link text |
143+
| `name` | `"page"` | Query parameter name |
144+
| `class` | `""` | CSS class for the link |
145+
| `disabledClass` | `"disabled"` | CSS class for disabled state |
146+
| `showDisabled` | `true` | Show disabled span or return empty string |
147+
| `pageNumberAsParam` | `true` | Page as query param vs route segment |
148+
149+
## Multiple Paginated Queries
150+
151+
Use the `handle` parameter when paginating multiple queries:
152+
153+
```cfm
154+
<!--- Controller --->
155+
users = model("User").findAll(page=params.userPage, perPage=10, handle="users", order="name");
156+
posts = model("Post").findAll(page=params.postPage, perPage=5, handle="posts", order="title");
157+
158+
<!--- View --->
159+
<h2>Users</h2>
160+
#paginationNav(handle="users")#
161+
162+
<h2>Posts</h2>
163+
#paginationNav(handle="posts")#
164+
```
165+
166+
## Changing Defaults
167+
168+
Override defaults in `config/settings.cfm`:
169+
170+
```cfm
171+
<cfscript>
172+
// Change default text
173+
set(functionName="previousPageLink", text="&laquo; Prev");
174+
set(functionName="nextPageLink", text="Next &raquo;");
175+
176+
// Change info format
177+
set(functionName="paginationInfo", format="Page [currentPage] of [totalPages]");
178+
179+
// Larger page number window
180+
set(functionName="pageNumberLinks", windowSize=5);
181+
</cfscript>
182+
```

0 commit comments

Comments
 (0)