news 2026/10/13 12:33:25

ngx-bootstrap Pagination 基础示例解析:默认翻页行为、按钮禁用逻辑与源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ngx-bootstrap Pagination 基础示例解析:默认翻页行为、按钮禁用逻辑与源码实现
  • UI组件
  • 前端

【免费下载链接】ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)

项目地址:https://gitcode.com/gh_mirrors/ng/ngx-bootstrap
点击查看免费下载

本文以 ngx-bootstrap 官方用例规格 pagination.examples.basic.use-case.md 为骨架,完整拆解 Pagination 组件"基础示例(Basic)"的用户场景、默认行为与底层源码实现。读者读完可以掌握<pagination>组件的三行最小用法、激活页与上一页/下一页按钮禁用状态的判定规则,以及这些行为在 pagination.component.ts 与 pagination.component.html 中是如何落地的,并了解对应单元测试的验证方式。

场景概述:Basic 示例要演示什么

该用例规格定义了一个非常聚焦的目标:向用户展示 Pagination 组件的基础分页功能。它的参与角色与范围如下:

  • Primary Actor(主要参与者):User(用户)
  • Scope(范围):Ngx-bootstrap DEMO,兼容 Bootstrap 版本 3 与 4
  • Goal(目标):向用户展示基本的分页(basic pagination)功能

整个场景围绕"打开演示页 → 进入 Basic 子菜单 → 在不同页码间来回切换"展开,重点验证三件事:哪一页处于 active(激活)状态、上一页/下一页按钮何时禁用、以及页面切换后状态如何迁移。

主成功场景:用户与分页组件的完整交互路径

用例规格的 Main success scenario 描述了一条连贯的操作链路,这正是 Basic 示例被设计出来的初衷:

  1. 用户打开 Pagination 演示页面;
  2. 用户点击 Basic 子菜单;
  3. 默认情况下第 1 页处于激活状态,且 Previous(上一页)按钮为禁用状态;
  4. 用户点击第 3 页后,Next(下一页)按钮变为禁用,Previous 按钮变为可用,第 1 页取消激活,第 3 页变为激活;
  5. 用户再点击第 2 页后,Previous 与 Next 按钮均可用,第 2 页激活,其余页均不激活。

该规格还包含一条 Variations(变体)路径:用户可能不是通过点击子菜单,而是直接滚动(scroll)到 "Basic" 示例区块再开始操作,交互结果与主场景一致。

需要说明的是,用例规格中假设演示数据可以翻出 3 页,这一假设与 demo 实际配置完全吻合:Basic 示例设置totalItems = 30,而分页组件默认itemsPerPage = 10,30 条数据恰好分成 3 页(见下文"最小可运行示例"与"页数计算"小节)。

最小可运行示例:Basic 示例的真实代码

Basic 示例在文档站中的实现位于 libs/doc-pages/pagination/src/lib/demos/basic/ 目录,组件定义与模板各只有几行:

组件类 basic.ts:

import { Component } from '@angular/core'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector selector: 'demo-pagination-basic', templateUrl: './basic.html', standalone: false }) export class DemoPaginationBasicComponent {}

模板 basic.html:

<pagination [totalItems]="30"></pagination>

也就是说,只需一个属性绑定,就能获得一个可交互的完整分页条:totalItems传入总数据条数 30,其余全部走默认配置。该示例组件被注册进文档站的演示列表 pagination-section.list.ts,同时出现在 "Overview" 与 "Examples" 两个分区的 Basic 条目中(anchor 分别为basic与basic-ex)。

在自己项目中复用时,可以通过两种方式引入分页模块(见 usage.md):

// Standalone 组件用法 import { PaginationModule } from 'ngx-bootstrap/pagination'; @Component({ standalone: true, imports: [PaginationModule, ...] }) export class AppComponent {}
// 传统 NgModule 用法 import { PaginationModule } from 'ngx-bootstrap/pagination'; @NgModule({ imports: [PaginationModule, ...] }) export class AppModule {}

PaginationModule 内部同时导出PaginationComponent与PagerComponent,因此上述引入即可同时使用<pagination>和<pager>两个组件。

默认行为背后的源码实现

Basic 场景中"第 1 页激活、Previous 禁用"以及"点到最后、Next 禁用"都不是魔法,而是由 pagination.component.ts 中的几个核心方法直接驱动的。

页数计算:为什么 30 条数据正好是 3 页

组件的totalItemsInput与itemsPerPageInput是两个被effect监听响应式输入,任一变化都会触发总页数重算(pagination.component.ts)。页数计算逻辑在calculateTotalPages()(pagination.component.ts):

protected calculateTotalPages(): number { const totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil(this.totalItems / this.itemsPerPage); return Math.max(totalPages || 0, 1); }

将 Basic 示例的数值代入:Math.ceil(30 / 10) = 3,于是渲染出 1、2、3 三个页码。注意两个边界规则:当itemsPerPage < 1时,所有条目会被合并到一页;计算结果至少为 1,保证空数据时也有一条可展示的分页条。

激活页与按钮禁用判定

Previous / Next 按钮是否禁用,由两个方法决定(pagination.component.ts):

noPrevious(): boolean { return this.page === 1; } noNext(): boolean { return this.page === this.totalPages; }
  • 当page === 1(第 1 页)时,Previous 禁用——这正是用例规格第 3 步描述的状态;
  • 当page === totalPages(最后一页,本例为第 3 页)时,Next 禁用——对应规格第 4 步"点击第 3 页后 Next 禁用";
  • 处于中间页(第 2 页)时,两个条件均不成立,两个按钮同时可用——对应规格第 5 步。

模板中的渲染逻辑位于 pagination.component.html 与 pagination.component.html:方向按钮通过[class.disabled]="noPrevious() || disabled()"/[class.disabled]="noNext() || disabled()"挂上 Bootstrap 的.disabled类;页码链接则通过[class.active]="pg.active"标记当前激活页。按钮点击统一走selectPage(page - 1, $event)/selectPage(page + 1, $event)。

点击翻页的核心方法

selectPage()是用户每次点击页码或方向按钮时都会命中的入口(pagination.component.ts):

selectPage(page: number, event?: Event): void { if (event) { event.preventDefault(); } if (!this.disabled()) { if (event && event.target) { const target: any = event.target; target.blur(); } this.writeValue(page); this.onChange(this.page); } }

它做了三件事:阻止<a href>的默认跳转、让被点击的链接失焦(避免残留焦点样式)、写入新页码。writeValue()进而触发page的 setter(pagination.component.ts),setter 中有一个值得注意的防越界逻辑:this._page = value > this.totalPages ? this.totalPages : value || 1——即使点击超出范围的页码也不会越界,会被钳制在最后一页;若新旧页码不同,则向外发出pageChanged事件,携带{ page, itemsPerPage }。

页码列表的生成

渲染出的页码集合由getPages()生成(pagination.component.ts):默认情况下(未设置maxSize或maxSize >= totalPages)直接罗列 1 到总页数的全部页码,并用makePage(num, num.toString(), num === currentPage)为当前页标记active: true。当设置了maxSize且总页数超过该上限时,才会进入"只显示部分页码"的逻辑——rotate为true时当前页居中,否则按页组滚动并在组间插入...跳页链接。Basic 示例不涉及maxSize,因此 3 个页码会完整展示。

状态机推演:第 1 页 → 第 3 页 → 第 2 页

把用例规格的交互链路映射到上述源码,可以得到一份精确的状态迁移表(以 30 条数据、每页 10 条、共 3 页为例):

操作前状态用户操作操作后状态对应源码依据
初始:page = 1无(页面加载)第 1 页active;Previous 禁用;Next 可用noPrevious()为 true;makePage(1, '1', true)
第 1 页点击第 3 页第 3 页active,第 1 页取消激活;Previous 可用;Next 禁用page = 3 = totalPages,noNext()为 true
第 3 页点击第 2 页第 2 页active,其余不激活;Previous、Next 均可用page = 2位于 1 与 3 之间,两个判定均为 false

这与用例规格第 3、4、5 步的预期完全一致。注意每步点击时pageChanged事件都会携带当前页号触发,方便外部监听(见下文"常用扩展")。

测试如何锁定这些默认行为

Basic 场景描述的行为并不是停留在文档层面的"愿望清单",而是有单元测试逐条锁定的。在 src/pagination/testing/pagination.component.spec.ts 中可以找到对应的验证:

  • 默认值测试:设置disabled = true后断言渲染出 3 个<li>(Previous、页码 1、Next),且首尾两个<li>都带disabled类(pagination.component.spec.ts);
  • 多页测试:totalItems = 10, itemsPerPage = 4时断言总页数为 3、第 1 页激活、Previous 禁用、Next 可用(pagination.component.spec.ts);
  • 点击翻页测试(模拟规格操作):以totalItems = 30, itemsPerPage = 4(共 8 页)模拟"点击第 5 页 → 第 5 页激活、Previous 变为可用"与"点击第 8 页 → Next 变为禁用、第 8 页激活"(pagination.component.spec.ts)。这与用例规格"点击最后一页 Next 禁用"的断言模式一致。

测试中还覆盖了带maxSize与boundaryLinks的点击场景(pagination.component.spec.ts),并验证了点击已禁用的 First 链接不会改变当前页。

常用扩展:从基础示例出发的进阶用法

Basic 示例刻意保持最简,而围绕它的更多行为可以平滑地扩展到相邻用例(同目录 docs/spec/pagination/ 下还有多份 use-case 文档):这里列出几个与基础交互直接相关的能力,均可在 libs/doc-pages/pagination/src/lib/demos/ 下找到对应演示。

双向绑定当前页与翻页事件

通过[(ngModel)]双向绑定当前页码,并用(pageChanged)监听翻页结果(page-changed-event.html):

<pagination [totalItems]="66" [(ngModel)]="currentPage" (pageChanged)="pageChanged($event)"></pagination>

事件回调接收PageChangedEvent对象(page-changed-event.ts):

pageChanged(event: PageChangedEvent): void { this.page = event.page; }

组件实现了ControlValueAccessor(pagination.component.ts),因此天然兼容 Angular 表单模型与ngModel双向绑定。

开关边界链接与方向链接

  • boundaryLinks = true时显示 First / Last 按钮(默认隐藏,见 boundary-links.html);
  • directionLinks = false时隐藏 Previous / Next 按钮(默认显示,见 direction-links.html)。

对应模板逻辑在 pagination.component.html 与 pagination.component.html 中:边界与方向按钮都受各自@if守卫控制,且同样遵循noPrevious()/noNext()的禁用规则。

整体禁用

disabled = true时所有分页链接不可点击(disabled.html)。源码中selectPage()会在this.disabled()为真时直接返回(pagination.component.ts),模板中非激活页也统一挂上disabled类(pagination.component.html)。

自定义链接文本

基础场景之外,还可以通过previousText、nextText、firstText、lastText替换按钮文字,例如 custom-links-content.html 使用&lsaquo;、&rsaquo;、&laquo;、&raquo;作为符号按钮。

默认配置速查

Basic 示例"零配置"即可工作的底气来自 PaginationConfig,组件构造时若未显式传配置,会调用configureOptions(paginationConfig.main)装载默认值(pagination.component.ts)。main配置如下:

main: Partial<ConfigModel> = { itemsPerPage: 10, boundaryLinks: false, directionLinks: true, firstText: 'First', previousText: 'Previous', nextText: 'Next', lastText: 'Last', pageBtnClass: '', rotate: true };

这与 Basic 示例的渲染结果一一对应:每页 10 条(所以 30 条数据分为 3 页)、无 First/Last 按钮、显示 Previous/Next、按钮文字为英文默认值。完整的ConfigModel结构定义在 models/index.ts,同文件还定义了PagesModel(每个页链接的文本、页码与激活标记)以及模板定制所需的PaginationLinkContext/PaginationNumberLinkContext上下文接口。

小结

通过对 pagination.examples.basic.use-case.md 的逐条拆解可以看到:Basic 示例虽然只用了<pagination [totalItems]="30"></pagination>一行模板,但它完整承载了分页组件的三条核心契约——当前页永远只有一个是激活的、首尾页会分别禁用 Previous/Next、任意切换都实时更新激活态。这三条契约在 pagination.component.ts 的calculateTotalPages()、noPrevious()/noNext()、selectPage()与getPages()中逐行实现,并被 pagination.component.spec.ts 的点击测试逐一验证。掌握这个最小闭环,再向边界链接、方向链接、禁用、页数限制等扩展用例(docs/spec/pagination/ 下的其余 use-case 文档)推进,就能完整驾驭 ngx-bootstrap 的分页能力。

  • UI组件
  • 前端

【免费下载链接】ngx-bootstrap

Fast and reliable Bootstrap widgets in Angular (supports Ivy engine)

项目地址:https://gitcode.com/gh_mirrors/ng/ngx-bootstrap
点击查看免费下载
上一篇:FanControl中文设置终极指南:5步让Windows风扇控制说中文
下一篇:同一 Agent、两个框架:在 awesome-agentic-ai-zh 中用 LangGraph 与 CrewAI 实战 search + summarize 对比

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 12:33:04

端侧Agent落地、多模态上下文与开源实战:AI日报详解技术选型与踩坑

2026年10月7日&#xff0c;AI圈的节奏还是快得让人喘不过气。今天这份日报&#xff0c;我照例从早上刷到傍晚&#xff0c;粗略过了一遍社群、技术社区和几个项目仓库的动态&#xff0c;大概两百多条消息。先给个整体感受&#xff1a;今天没有那种炸裂级别的大新闻&#xff0c;不…

作者头像 李华
网站建设 2026/10/13 12:32:33

在线期末考试主观题答题框架与时间分配实战指南

1. 项目背景与整体设计思路1.1 这个项目到底在做什么“2020南京邮电大学Mooc—在线期末考试主观卷”这个标题&#xff0c;核心指向的是一套在线课程期末考试中主观题部分的完整应对方案。它不是一个单纯的题库&#xff0c;也不是一份简单的复习提纲&#xff0c;而是一套围绕“在…

作者头像 李华
网站建设 2026/10/13 12:31:36

Vuforia Engine 9.4.6 Unity集成:从RAR包导入到AR识别参数配置与踩坑指南

简介&#xff1a;Vuforia Engine 9.4.6 离线缓存包是面向 Unity 开发者的增强现实&#xff08;AR&#xff09;开发插件&#xff0c;由 PTC 公司提供&#xff0c;主要解决无互联网环境下 Vuforia 组件安装与集成的难题。该包适用于图像识别、目标追踪、3D 模型渲染、云目标等主流…

作者头像 李华
网站建设 2026/10/13 12:31:06

基于C#的PLC工位追溯与报警提示系统开发实战解析

1. 项目背景与需求拆解搞制造业自动化的朋友应该都有体会&#xff0c;产线上最怕的不是设备停机&#xff0c;而是"出了事查不清"。我去年在某汽车零部件车间帮人做了一套基于C#的PLC工位间追溯与报警提示软件&#xff0c;核心就是为了解决"一台产品经过哪些工位…

作者头像 李华
网站建设 2026/10/13 12:29:26

多语言多领域机器翻译系统搭建:术语一致性与领域适配全解析

说起多语言多领域机器翻译系统的搭建&#xff0c;我先讲一个让我印象深刻的场景&#xff1a;某电商平台把中文商品描述直接丢给通用翻译引擎&#xff0c;出来的是“医用一次性防护服”被翻成“医疗用一次性的保护性衣服”&#xff0c;而平台方坚持要术语表里的标准译法。这个场…

作者头像 李华
网站建设 2026/10/13 12:28:19

MES工位执行状态持久化:状态模型与Redis协同设计

做MES和智能工厂这块时间久了&#xff0c;会发现一个很诡异的现象&#xff1a;大家前期讨论方案时&#xff0c;最关注的都是排产算法、物料拉动、质量管理这些大模块&#xff0c;真正上线跑起来之后&#xff0c;台账上线了&#xff0c;工单也派下去了&#xff0c;反而是L3层工位…

作者头像 李华