TanStack Angular Table 行固定(Row Pinning)完整指南:从状态管理到三区渲染

TanStack Angular Table 行固定(Row Pinning)完整指南:从状态管理到三区渲染 前端UI组件【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址https://gitcode.com/gh_mirrors/ta/table点击查看免费下载本篇技术指南以 docs/framework/angular/guide/row-pinning.md 为核心骨架系统讲解 tanstack/angular-table 中行固定功能的完整用法如何通过rowPinningFeature开启功能、用四种方式管理固定状态、借助 Row/Table 级 API 构建置顶/置底控件以及如何理解固定行在过滤、分页与分组场景下的底层行为。读完本文你将能独立实现一个支持顶部固定、底部固定、行级禁用与三区渲染的 Angular 数据表格。一、什么是 Row Pinning固定行与三区行模型行固定Row Pinning允许你把选中的行固定在表格的顶部区域或底部区域其余行在中部区域正常渲染。这在展示汇总行、关键操作行、合计行或需要持续可见的数据时非常有用典型应用包括把合计行固定在表格底部、把重要操作行固定在顶部、或在长列表滚动时让关键行不离开视野。从 rowPinningFeature.types.ts 的定义可以看出固定的本质是按行 ID 记录分区export type RowPinningPosition false | top | bottom export interface RowPinningState { bottom: Arraystring top: Arraystring }即top数组存放固定到顶部的行 IDbottom数组存放固定到底部的行 IDfalse表示不固定处于中间区。固定后当前行模型被拆分为三个行列表table.getTopRows() // 顶部固定行 table.getCenterRows() // 中部未固定行 table.getBottomRows() // 底部固定行在 table-core 的实现中getTopRows/getBottomRows按state.rowPinning.top/bottom数组的顺序解析行并给每行打上position top | bottom标记见 rowPinningFeature.utils.ts而getCenterRows则把同时出现在 top/bottom 中的行 ID 集合从当前行模型中过滤掉见 table_getCenterRows。固定行还遵循先固定、后排序的特性顺序详见下文与排序特性的协作顺序。二、启用行固定注册 rowPinningFeature在 TanStack Angular Table v9 中所有功能均通过tableFeatures({ ... })显式注册。启用行固定只需加入rowPinningFeature并且不需要任何行模型工厂如createFilteredRowModel、createSortedRowModel之类因为它只对已经生成的行模型做切片划分import { signal } from angular/core import { injectTable, tableFeatures, rowPinningFeature, } from tanstack/angular-table const features tableFeatures({ rowPinningFeature }) export class App { readonly data signal(defaultData) readonly table injectTable(() ({ features, columns, data: this.data(), })) }注意features是模块级常量应在injectTable之外定义并复用。injectTable的选项工厂函数会在其内部读取到的任意 signal 变化时被重新求值见 injectTable.ts 的注释说明因此features、columns这类昂贵或静态的值应保持在工厂外只在工厂内读取data()等响应式状态。注册该特性后表格会获得一组完整的固定相关 API同时状态切片rowPinning也自动加入表格 store。从 rowPinningFeature.ts 可以看到特性的getInitialState会把默认状态{ top: [], bottom: [] }与initialState.rowPinning合并因此未配置时两个区域均为空。三、管理固定状态的四种方式固定状态本质上是一份{ top: string[], bottom: string[] }数据tanstack/angular-table 提供四种管理入口按推荐程度依次介绍。3.1 用 initialState 设置默认固定行如果固定行在表格首次渲染时就应生效例如首行置顶、末行置底直接在initialState.rowPinning中声明行 ID 即可readonly table injectTable(() ({ features, columns, data, initialState: { rowPinning: { top: [0], bottom: [3], }, }, }))initialState只影响初始状态此后用户通过界面固定/取消固定会覆盖它。当调用table.resetRowPinning()时状态会回到这份初始值。3.2 用外部 Atom 管理v9 推荐当需要在表格实例之外读取或写入固定状态时官方推荐使用外部原子external atom——即用createAtom创建原子并通过atoms选项传入import { createAtom } from tanstack/angular-store import type { RowPinningState } from tanstack/angular-table export class App { readonly rowPinningAtom createAtomRowPinningState({ top: [], bottom: [], }) readonly table injectTable(() ({ features, columns, data: this.data(), atoms: { rowPinning: this.rowPinningAtom, }, })) // 在应用任意位置读取固定状态 // this.rowPinningAtom.get() }外部原子的核心优势是细粒度订阅任何组件都可以用rowPinningAtom.get()读取、用rowPinningAtom.set(...)或update(...)写入而无需在每次状态变化时重新执行injectTable的选项工厂。这与源码中atoms 优先的设计一致——核心实现中table.atoms.rowPinning?.get()是读取固定状态的统一入口见 rowPinningFeature.utils.ts而setRowPinning最终也会路由到该 slice 的更新逻辑。3.3 用 signal 自持状态v8 风格兼容迁移v8 风格的state.rowPinningonRowPinningChange模式在 v9 中仍然受支持。在 Angular 中这意味着用组件的 signal 自己拥有这份状态readonly rowPinning signalRowPinningState({ top: [], bottom: [], }) readonly table injectTable(() ({ features, columns, data, state: { rowPinning: this.rowPinning(), }, onRowPinningChange: (updater) typeof updater function ? this.rowPinning.update(updater) : this.rowPinning.set(updater), }))此模式适合简单集成或从 v8 迁移的存量代码但粒度不如外部原子细。更深入的状态管理对比可参考 Table State 指南。从源码角度onRowPinningChange是特性注册时由makeStateUpdater(rowPinning, table)生成的默认选项见 rowPinningFeature.ts当状态来自外部signal 或 atom时你提供自己的回调覆盖它。3.4 编程式更新setRowPinning 与 resetRowPinning无论状态由谁持有都可以通过表格实例直接更新table.setRowPinning({ top: [0, 2], bottom: [8], }) table.resetRowPinning() // 重置为 initialState.rowPinning table.resetRowPinning(true) // 强制清空 top/bottom 两个数组setRowPinning接受新状态对象或 updater 函数两种形式resetRowPinning不带参数时克隆initialState.rowPinning若未配置则回退到空数组传true时忽略初始状态、直接重置为空。这一语义在 table_resetRowPinning 中实现defaultState ? getDefaultRowPinningState() : cloneState(initialState.rowPinning ?? getDefaultRowPinningState())。四、用 Row 级 API 构建固定控件每一行都暴露了三个固定相关 API用于判断能否固定、固定在哪儿、在固定区中的序号row.getCanPin() // 该行是否允许固定 row.getIsPinned() // 返回 top | bottom | false row.getPinnedIndex() // 该行在其固定区内的可见序号未固定为 -1 row.pin(top) // 固定到顶部 row.pin(bottom) // 固定到底部 row.pin(false) // 取消固定回到中部其中getIsPinned的实现就是检查top/bottom数组是否包含该行 ID见 row_getIsPinnedgetPinnedIndex则取对应固定区的可见行序列求下标见 row_getPinnedIndex。利用这些 API 可以在每一行渲染固定的操作按钮if (row.getCanPin()) { div button typebutton (click)row.pin(top) [disabled]row.getIsPinned() top Top /button button typebutton (click)row.pin(false) [disabled]!row.getIsPinned() Center /button button typebutton (click)row.pin(bottom) [disabled]row.getIsPinned() bottom Bottom /button /div }4.1 pin 的 includeLeafRows 与 includeParentRowsrow.pin还接受两个可选标志row.pin(position, includeLeafRows?, includeParentRows?)includeLeafRows为true时该行的所有叶子行含展开的子行随之一同固定includeParentRows为true时该行的所有父行随之一同固定。这对分组/展开场景非常关键当你固定一个分组父行时可以选择只固定父行本身还是把整组子行leaf rows一起带走。源码中的实现会把父行 ID、自身 ID、叶子行 ID 合并为一个集合然后统一从另一个区域移除并加入目标区域见 row_pin。扩展提示在示例应用 app.html 中这两个标志被做成复选框演示了固定时连带叶子行/父行的交互效果当row.getIsPinned()为真时按钮显示为取消固定Unpin/x点击row.pin(false, ...)即可放回中部。五、用 Table 级 API 做三区渲染固定后行模型被拆分为三个列表表格渲染时应分别遍历tbody for (row of table.getTopRows(); track row.id) { tr classpinned !-- 渲染顶部固定行 -- /tr } for (row of table.getCenterRows(); track row.id) { tr !-- 渲染中部行 -- /tr } for (row of table.getBottomRows(); track row.id) { tr classpinned !-- 渲染底部固定行 -- /tr } /tbody固定行本身并不会被置顶/置底——表格库只负责按区域切分行列表视觉上的置顶/置底与滚动跟随需要你在模板/CSS 中实现例如给.pinned行加position: sticky或把三个区域放入独立的滚动容器。这就是 headless UI 的设计哲学逻辑与状态完全由库管理展示层完全由你掌控。辅助 APItable.getIsSomeRowsPinned(position?)可用来判断是否存在固定行可选限定区域table.getIsSomeRowsPinned() // 任意区域存在固定行 table.getIsSomeRowsPinned(top) // 顶部存在固定行 table.getIsSomeRowsPinned(bottom) // 底部存在固定行其实现即检查对应数组的长度是否为 0见 table_getIsSomeRowsPinned常用于存在固定行时显示清除按钮之类的 UI 分支。六、与排序特性的协作顺序行固定不是唯一能重排行序的特性。tanstack/angular-table 中有两个能改变行顺序的特性执行顺序固定为Row Pinning——若存在固定行先被拆分为 top固定、center未固定、bottom固定三区Sorting——排序基于固定结果进行。也就是说排序不会影响哪些行被固定但会影响中部区域以及固定区内部的排列顺序。完整的排序用法见 Sorting 指南。事实依据该执行顺序是官方文档明示的约定见 row-pinning.md There are 2 table features that can reorder rows一节在组合使用分组、排序、固定时需要牢记先分区、后排序。七、禁用固定enableRowPinning默认情况下所有行都允许固定enableRowPinning默认为true。你可以对整个表统一禁用也可以按行用谓词函数判断readonly table injectTable(() ({ features, columns, data, enableRowPinning: row row.original.status ! archived, }))传入布尔值则全局生效传入函数则按行判断row.getCanPin()返回的正是这个配置的求值结果。源码中 row_getCanPin 的逻辑为函数形式直接调用谓词布尔形式取enableRowPinning ?? true。类型定义上它同时接受boolean | ((row) boolean)见 rowPinningFeature.types.ts。示例应用还给出了另一种常见写法——只允许满足条件的行固定// 仅允许 age 18 的行固定 // enableRowPinning: row row.original.age 18见 app.ts 中注释掉的示例。八、keepPinnedRows固定行是否脱离过滤与分页keepPinnedRows控制固定行在过滤/分页下的可见性默认值为truekeepPinnedRows: true默认固定行即使被过滤掉、或不在当前分页范围内依然渲染在其固定区域中。固定行永远可见keepPinnedRows: false固定行只有在当前过滤分页后的行模型中真实存在时才渲染否则不显示。readonly table injectTable(() ({ features, columns, data, keepPinnedRows: false, }))实现层面table_getPinnedRows在keepPinnedRows为真时从分页前的行模型getPrePaginatedRowModel必要时回退到核心行模型按 ID 取行并额外检查该行的所有父行是否已展开为假时则在当前可见行中查找见 table_getPinnedRows。需要注意keepPinnedRows并不豁免展开expanding——固定行的父行若被折叠固定行同样不会渲染。在示例应用 app.ts 中keepPinnedRows被做成一个 checkbox 绑定到 signal配合 20 行/页的分页设置可以直观对比固定行随分页消失与固定行始终可见两种行为同文件也演示了state.rowPinningonRowPinningChange的 v8 兼容写法initialState.rowPinning注释中保留了首屏固定第 0 行与第 1 行的示例见 app.ts。九、端到端验证与测试固定功能的正确性由两层测试保障核心单测rowPinningFeature.utils.test.ts 覆盖了table_setRowPinning的 updater 分发、table_resetRowPinning的默认值语义、getDefaultRowPinningState每次返回新对象、以及row_pin在 top/bottom/false 三种位置下的数组增删逻辑还包括未注册该特性的纯核心表对静态函数的容错处理Angular 示例 E2Esmoke.spec.ts 通过 Playwright 启动示例应用验证表格正常渲染、首行数据渲染正确并覆盖Regenerate Data按钮触发的数据刷新场景同时收集页面错误确保无控制台异常。你可以在 examples/angular/row-pinning 目录中查看完整可运行的示例工程含 app.ts 组件逻辑、app.html 模板、makeData.ts 数据生成器它同时演示了行固定与展开、过滤、列宽、分页的组合用法是理解本节所有 API 的最佳参考。十、小结与推荐实践功能注册在tableFeatures({ rowPinningFeature })中加入特性即可无需行模型工厂状态管理优先用外部 atomatoms.rowPinning获得细粒度订阅简单场景可用initialState.rowPinning或 v8 风格 signal onRowPinningChange控件构建用row.getCanPin()、row.getIsPinned()驱动按钮状态row.pin(position, includeLeafRows?, includeParentRows?)触发固定分组表按需连带叶子行/父行渲染分别遍历getTopRows()/getCenterRows()/getBottomRows()三个列表视觉置顶/置底由模板与 CSS 负责行为控制enableRowPinning控制可固定性keepPinnedRows决定固定行是否豁免过滤与分页默认豁免顺序约定先固定、后排序重置用table.resetRowPinning()不传参回初始状态传true清空。相关指南Table State、Sorting、Expanding、Pagination。赞分享前端UI组件【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址https://gitcode.com/gh_mirrors/ta/table点击查看免费下载相关推荐TanStack Alpine Table 行固定Row Pinning完全指南从状态管理到模板渲染TanStack Alpine Table 行固定Row Pinning完全指南从状态管理到模板渲染 行固定Row Pinning允许你把选中的行固定前端UI组件TanStack Alpine Table 行选择Row Selection实战指南从状态管理到 UI 渲染TanStack Alpine Table 行选择Row Selection实战指南从状态管理到 UI 渲染 TanStack Table 的行选择Ro前端UI组件揭秘MousecapemacOS鼠标光标个性化深度解析揭秘MousecapemacOS鼠标光标个性化深度解析 厌倦了macOS单调的白色箭头光标想要为你的桌面体验注入个性色彩Mousecape正是你寻找的解决前端UI组件上一篇JAX 段归约操作指南jax.ops.segment_sum / segment_max 系列函数与 .at 索引更新全面解析下一篇Web Starter Kit与TypeScript集成提升代码质量与可维护性创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考