Marta Ruiz
Product Designer
Email: marta.ruiz@example.com
Phone: +34 600 111 222
Angular 数据表格和分页列表,支持服务端分页、排序、筛选、选择、模板和 CSS 变量。
最后更新 2026年8月24日
当你需要服务端操作、选择、筛选和可复用的列表渲染,又不想在每个项目中重建表格逻辑时,请使用这个 Angular 数据表格和分页列表库。
npm install ng-hub-ui-paginableng-hub-ui-paginable 面向构建管理后台、搜索结果、CRM 页面和管理后台系统的 Angular 团队。它将一个可复用的表格和列表层,与生产中通常最重要的操作结合起来:分页、筛选、排序、选择和自定义模板。
基础的 table 渲染和分页。
表格基础用法
ID | Name | Email | Role |
|---|---|---|---|
| 1 | John Doe | john@example.com | Admin |
| 2 | Jane Smith | jane@example.com | Editor |
| 3 | Bob Johnson | bob@example.com | User |
| 4 | Alice Brown | alice@example.com | Editor |
无关的表单控件
ID | Name | Email | Role | Department |
|---|---|---|---|---|
| 1 | John Doe | john.doe@example.com | Admin | IT |
| 2 | Jane Smith | jane.smith@example.com | Editor | Marketing |
| 3 | Bob Johnson | bob.johnson@example.com | User | Sales |
| 4 | Alice Brown | alice.brown@example.com | Editor | IT |
| 5 | Charlie Wilson | charlie.wilson@example.com | User | HR |
Search and rows-per-page are rendered by ng-hub-ui-forms (hub-input / hub-select). Remove the provider and they fall back to native controls.
表格分页
ID | Name | Department | Position | Salary | Email |
|---|---|---|---|---|---|
| 1 | John Doe | Engineering | Senior Developer | 85000 | john.doe@company.com |
| 2 | Jane Smith | Marketing | Marketing Manager | 72000 | jane.smith@company.com |
| 3 | Bob Johnson | Engineering | Frontend Developer | 65000 | bob.johnson@company.com |
| 4 | Alice Brown | HR | HR Specialist | 58000 | alice.brown@company.com |
| 5 | Charlie Wilson | Sales | Sales Representative | 55000 | charlie.wilson@company.com |
表格的 CSS 变量
ID | Name | Email | Role |
|---|---|---|---|
| 1 | John Doe | john@example.com | Admin |
| 2 | Jane Smith | jane@example.com | Editor |
| 3 | Bob Johnson | bob@example.com | User |
| 4 | Alice Brown | alice@example.com | Reviewer |
ID | Name | Email | Role |
|---|---|---|---|
| 1 | John Doe | john@example.com | Admin |
| 2 | Jane Smith | jane@example.com | Editor |
| 3 | Bob Johnson | bob@example.com | User |
| 4 | Alice Brown | alice@example.com | Reviewer |
排序、过滤、选择以及服务端操作。
基础排序与筛选
ID | Name | Role | Status |
|---|---|---|---|
| 4 | Alice Brown | Editor | Active |
| 3 | Bob Johnson | User | Pending |
| 5 | Charlie Wilson | User | Deleted |
| 2 | Jane Smith | Editor | Inactive |
| 1 | John Doe | Admin | Active |
高级筛选(菜单与范围)
ID | Product | Price ($) | Added Date | Category |
|---|---|---|---|---|
| 1 | Laptop | 1200 | Sun Oct 01 2023 00:00:00 GMT+0000 (Coordinated Universal Time) | Electronics |
| 2 | Mouse | 25 | Wed Nov 15 2023 00:00:00 GMT+0000 (Coordinated Universal Time) | Electronics |
| 3 | Keyboard | 75 | Mon Nov 20 2023 00:00:00 GMT+0000 (Coordinated Universal Time) | Electronics |
| 4 | Desk Lamp | 45 | Tue Dec 05 2023 00:00:00 GMT+0000 (Coordinated Universal Time) | Office |
| 5 | Chair | 250 | Sun Dec 10 2023 00:00:00 GMT+0000 (Coordinated Universal Time) | Office |
批量选择与操作
ID | Name | Email | Role | |
|---|---|---|---|---|
| 1 | John Doe | john@example.com | Admin | |
| 2 | Jane Smith | jane@example.com | Editor | |
| 3 | Bob Johnson | bob@example.com | User | |
| 4 | Alice Brown | alice@example.com | Editor | |
| 5 | Charlie Wilson | charlie@example.com | User |
批量操作(batchActions)
ID | Name | Email | Status | |
|---|---|---|---|---|
| 1 | John Doe | john.doe@example.com | Active | |
| 2 | Jane Smith | jane.smith@example.com | Active | |
| 3 | Bob Johnson | bob.johnson@example.com | Inactive | |
| 4 | Alice Brown | alice.brown@example.com | Active | |
| 5 | Charlie Wilson | charlie.wilson@example.com | Inactive |
服务端操作
ID | Name | Email | Role | Status |
|---|---|---|---|---|
| 1 | John Doe | user1@example.com | Admin | Active |
| 2 | Jane Smith | user2@example.com | Editor | Inactive |
| 3 | Bob Johnson | user3@example.com | User | Pending |
| 4 | Alice Brown | user4@example.com | Moderator | Active |
| 5 | Charlie Wilson | user5@example.com | Admin | Inactive |
| 6 | Diana Martinez | user6@example.com | Editor | Pending |
| 7 | Edward Taylor | user7@example.com | User | Active |
| 8 | Fiona Anderson | user8@example.com | Moderator | Inactive |
| 9 | George Lee | user9@example.com | Admin | Pending |
| 10 | Hannah White | user10@example.com | Editor | Active |
可展开行、自定义模板、响应式布局和动态样式。
可展开行与粘性列
ID | Name | Role | Email | Actions | |
|---|---|---|---|---|---|
| 1 | John Doe | Admin | john@example.com | ||
| 2 | Jane Smith | Editor | jane@example.com | ||
| 3 | Bob Johnson | User | bob@example.com | ||
| 4 | Alice Brown | Editor | alice@example.com |
多个固定列
ID | Full Name | Email Address | Department | Position | Office Location | Salary | Status |
|---|---|---|---|---|---|---|---|
| 1 | John Doe | john.doe@example.com | Engineering | Senior Developer | New York | $120,000 | Active |
| 2 | Jane Smith | jane.smith@example.com | Marketing | Marketing Manager | Los Angeles | $95,000 | Active |
| 3 | Bob Johnson | bob.johnson@example.com | Sales | Sales Representative | Chicago | $75,000 | On leave |
| 4 | Alice Brown | alice.brown@example.com | Engineering | Technical Lead | San Francisco | $140,000 | Active |
| 5 | Charlie Wilson | charlie.wilson@example.com | HR | HR Specialist | Boston | $65,000 | Active |
可主题化的表头与分隔线
Invoice | Client | Amount | Status |
|---|---|---|---|
| INV-1001 | Acme Corp | $3,200 | Paid |
| INV-1002 | Globex | $1,750 | Pending |
| INV-1003 | Initech | $980 | Paid |
| INV-1004 | Umbrella | $5,410 | Overdue |
滚动时固定表头
[stickyHeader] inside a plain scroll div, the second via the built-in options.scrollable frame. [stickyHeader] (any scroll container)ID | Name | Department | Location | Status |
|---|---|---|---|---|
| 1 | Employee 1 | Engineering | New York | On leave |
| 2 | Employee 2 | Marketing | London | Active |
| 3 | Employee 3 | Sales | Berlin | Active |
| 4 | Employee 4 | HR | Tokyo | Active |
| 5 | Employee 5 | Engineering | New York | Active |
| 6 | Employee 6 | Marketing | London | On leave |
| 7 | Employee 7 | Sales | Berlin | Active |
| 8 | Employee 8 | HR | Tokyo | Active |
| 9 | Employee 9 | Engineering | New York | Active |
| 10 | Employee 10 | Marketing | London | Active |
options.scrollable (built-in frame)ID | Name | Department | Location | Status |
|---|---|---|---|---|
| 1 | Employee 1 | Engineering | New York | On leave |
| 2 | Employee 2 | Marketing | London | Active |
| 3 | Employee 3 | Sales | Berlin | Active |
| 4 | Employee 4 | HR | Tokyo | Active |
| 5 | Employee 5 | Engineering | New York | Active |
| 6 | Employee 6 | Marketing | London | On leave |
| 7 | Employee 7 | Sales | Berlin | Active |
| 8 | Employee 8 | HR | Tokyo | Active |
| 9 | Employee 9 | Engineering | New York | Active |
| 10 | Employee 10 | Marketing | London | Active |
通过标签选择器进行深色重设主题
hub-table tag selector — no!important. ID | Name | Email | Role |
|---|---|---|---|
| 1 | John Doe | john@example.com | Admin |
| 2 | Jane Smith | jane@example.com | Editor |
| 3 | Bob Johnson | bob@example.com | User |
| 4 | Alice Brown | alice@example.com | Reviewer |
可自定义的展开图标
--hub-table-icon-caret-up / --hub-table-icon-caret-down. Product | SKU | Price | Stock | |
|---|---|---|---|---|
| Wireless Mouse | WM-100 | $24.99 | 132 | |
| Mechanical Keyboard | MK-220 | $89.00 | 47 | |
| USB-C Hub | UH-330 | $39.50 | 88 |
自定义图标集
--hub-table-icon-* variable. 一键主题(mixin)
Code | Name | Owner | |
|---|---|---|---|
| A-01 | Alpha | Ada | |
| B-02 | Bravo | Alan | |
| C-03 | Charlie | Grace |
使用模板进行自定义
ID | Name | Role | Status |
|---|---|---|---|
| 1 | John Doe | Admin | Active |
| 2 | Jane Smith | Editor | Active |
| 3 | Bob Johnson | User | Inactive |
响应式布局与状态
ID | Name | Role |
|---|---|---|
| 1 | John Doe | Admin |
| 2 | Jane Smith | Editor |
默认状态组件
ID | Name | Role |
|---|---|---|
| 1 | John Doe | Admin |
| 2 | Jane Smith | Editor |
| 3 | Bob Johnson | User |
动态行样式(rowClass)
ID | Name | Email | Role | Status |
|---|---|---|---|---|
| 1 | John Doe | john.doe@example.com | Admin | active |
| 2 | Jane Smith | jane.smith@example.com | Editor | inactive |
| 3 | Bob Johnson | bob.johnson@example.com | User | pending |
| 4 | Alice Brown | alice.brown@example.com | Editor | active |
| 5 | Charlie Wilson | charlie.wilson@example.com | User | deleted |
主从选择 (rowClass)
Order | Customer | Total | Status |
|---|---|---|---|
| 1001 | Ada Lovelace | $1,240.00 | Paid |
| 1002 | Alan Turing | $890.50 | Pending |
| 1003 | Grace Hopper | $2,415.00 | Paid |
| 1004 | Katherine Johnson | $540.75 | Refunded |
| 1005 | Edsger Dijkstra | $1,975.20 | Paid |
Click a row to open its detail — the active row shows the tint + accent bar.
Select an order to see its detail.
国际化(i18n)
ID | Name | Email | Role |
|---|---|---|---|
| 1 | John Doe | john@example.com | Admin |
| 2 | Jane Smith | jane@example.com | Editor |
| 3 | Bob Johnson | bob@example.com | User |
| 4 | Alice Brown | alice@example.com | Moderator |
| 5 | Charlie Wilson | charlie@example.com | Admin |
| 6 | Diana Martinez | diana@example.com | Editor |
| 7 | Edward Taylor | edward@example.com | User |
| 8 | Fiona Anderson | fiona@example.com | Moderator |
| 9 | George Lee | george@example.com | Admin |
| 10 | Hannah White | hannah@example.com | Editor |
| 11 | Ivan Clark | ivan@example.com | User |
| 12 | Julia Scott | julia@example.com | Moderator |
RTL 布局(从右到左)
ID | Name | Email | Role | |
|---|---|---|---|---|
| 1 | John Doe | john@example.com | Admin | |
| 2 | Jane Smith | jane@example.com | Editor | |
| 3 | Bob Johnson | bob@example.com | User | |
| 4 | Alice Brown | alice@example.com | Reviewer |
底部栏排序(CSS 变量)
ID | Name | Role |
|---|---|---|
| 1 | John Doe | Admin |
| 2 | Jane Smith | Editor |
| 3 | Bob Johnson | User |
灵活的列表视图,具备同样强大的分页和选择能力。
可分页与可选择列表
连接列表(时间线)
可选择列表
pointer cursor; tick the checkbox to select. []null齐平样式 — 列表,而非卡片堆叠
Each row is its own surface. On a page, that is what separates it from the page.
Inside a dialog the frame is already drawn, so the rows only need a rule between them.
Outer border, radius and head rule go; the row divider and the cell padding stay, because those are what keep a table readable across its columns.
Room | Kind | Rate |
|---|---|---|
| Despacho 1 | Office | from 900,00 € / month |
| Sala Timple | Meeting room | from 90,00 € / day |
| Zona compartida | Coworking | from 180,00 € / month |
可编辑——控件呈现为表格
Product | Quantity | Unit |
|---|---|---|
u. | box | |
u. | box | |
u. | kg |
flushFields — the same controls, read as a sheetProduct | Quantity | Unit |
|---|---|---|
u. | box | |
u. | box | |
u. | kg |
选择树形列表,以及点击传递的内容
[](none yet)列表的 CSS 变量
hub-list-theme() mixin卡片式列表
Product Designer
Email: marta.ruiz@example.com
Phone: +34 600 111 222
Frontend Engineer
Email: carlos.vega@example.com
Phone: +34 600 333 444
Engineering Manager
Email: lucia.moreno@example.com
Phone: +34 600 555 666
QA Lead
Email: pablo.serra@example.com
Phone: +34 600 777 888
嵌套列表
列表加载、错误和空状态
added: flushFields on hub-table: the controls in the cells drawn as a spreadsheet, not as a form. The fields lose their border and surface, a static addon reads as its own content rather than as a chip, and a projected button stops being welded to its neighbour — corners back, a gap, and its own border colour, since two actions in a cell are two things to press. Done by assigning ng-hub-ui-forms tokens to the cells, which is why it reaches your own cell templates; it needs forms 22.21.0 for the seam tokens.
fixed: A group row gets no radio in single selection. The control was drawn on every row, so in a list grouped with `bindChildren` a heading became one of the things to choose — picking a building answered with a building, which nobody can book. Only leaves carry a radio now, which is the single-selection twin of the cascade 22.12.0 gave `multiple`.
changed: BREAKING — `clickFn` hands over the item, not the internal wrapper. `ListClickEvent.item` carried the form group wrapping each row (`{selected, collapsed, data, children}`) while the published type said `item: T`, so a consumer reading `event.item.<field>` by the types got `undefined`: no error, no warning, every guard silently false. It is the item now; `value` reads `bindLabel` from the item rather than from the wrapper, and a new `children: T[]` hands over a group's children as items. Adding an alias and deprecating `item` was rejected: it would leave the library carrying, for ever, a field documented as the item that is not one, to protect code depending on a contradiction between the runtime and the type.
changed: BREAKING — a group row selects its children instead of itself. With `bindChildren`, a group's checkbox put the group's own value into the selection and left its children alone, so ticking a building meant "the building", which nobody can book, and a building with one room chosen looked exactly like one with none. Ticking a group now takes everything under it, a partly-selected group renders indeterminate, and only leaves travel in the value.
fixed: Rebuilding `items` no longer clears the selection, nor claims the user did. The setter emptied the form and published the empty selection through the CVA, so a list that merely re-read its data dropped the choice and told the consumer the user had cleared it, with no way to tell a refresh from an edit. The selection is carried across and matched by `bindValue`, only what the new items no longer offer falls out, and nothing is published unless something really went.
fixed: `setDisabledState` disables. It assigned a flag the template never read, so a disabled list still changed its selection. It now disables the form, reaching every `selected` control however deep — through the form and not a `[disabled]` binding, because Angular ignores that binding on a reactive control: it warns and the box stays live.
fixed: `options.searchable` searches. The component rendered a search box wired to a `filter()` whose body was entirely commented out. It filters now, with a `searchTerm` model and an optional `searchFn` — the same two names `hub-table` uses, so the components do not disagree about what "searchable" means. A group survives while any descendant matches, and submitting returns to the first page.
added: `flush` on `hub-list` and `hub-table`: the collection drawn as a list rather than as a stack of cards. Every row wears its own border, radius and surface by default — right for a collection standing on a page, wrong for a list of choices inside a dialog, where a bordered box per row reads as a region of its own and the surface has already drawn the frame. It had to be an input rather than something CSS can reach, and the reason is a trap the whole token catalogue shares: the defaults are declared on the host, which is the very element a consumer puts a class on, so their assignment ties on specificity and loses on source order — silently, which reads as "the token does nothing". Two tokens come with it, read only under the variant: --hub-list-divider-width and --hub-list-divider-color, the rule that stands in for the gap. It falls between siblings only, a group and the collection it opens keep none, and the cards display gets none at all because a grid separated by rules would draw them across the gaps. On the table the row divider survives on purpose.
fixed: The single-selection radio has a rule. It shipped with the mode in 22.10.0 and had no CSS at all — .hub-list__radio matched nothing in the component — so the control rendered at the browser's own size beside a themed list, visibly not part of it. It now takes --hub-list-radio-size, defaulting to the checkbox's since the two are the same control wearing a different rule about how many may be on, and the list's accent.
fixed: Row-action icons sat low in their buttons. The button took its height from the line box of the glyph, and an icon font glyph is an inline-block resting on the baseline, so a 16px glyph in a 24px line box left the descender gap underneath — 4px above and 8px below on a 28px button. The content row now carries an explicit height and centres the glyph in it: 28px tall, 6/6 exactly. Worth recording that both obvious repairs make it worse — display: flex on the button or the icon host removes the line box that sets the height and collapses it to 20px, and vertical-align: middle overshoots to 7.78/4.22.
changed: Translation lookups now resolve HUBUI.PAGINABLE.* before the legacy flat keys. The table, list and paginator provide the namespace through HUB_TRANSLATION_PREFIX, so an application dictionary can feed them via provideHubTranslationAdapter() without reserving generic top-level keys. Existing flat dictionaries keep working — the bare key is still the fallback.
added: README documentation for the application-wide translation adapter (provideHubTranslationAdapter() from ng-hub-ui-utils).
removed: Removed the @angular/animations peer dependency and the unused table fadeInOut trigger declaration. The package is deprecated upstream and the trigger was never bound in the template. Applications that installed it only for ng-hub-ui-paginable can drop it.
fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.
added: --hub-table-head-border-width — the header's bottom-border thickness, defaulting to the shared --hub-table-border-width (zero visual change); set it to calc(var(--hub-table-border-width) * 2) for the Bootstrap-style thicker header and it keeps following the base width.
added: --hub-table-head-text-transform (none) and --hub-table-head-letter-spacing (normal) on the header titles — uppercase or tracked column headers without reaching for a th rule.
fixed: Importing the package no longer crashes under SSR: the module-scope navigator access behind DEFAULT_LANGUAGE is now guarded.
removed: Dead internal ViewsService (never exported; its only working method threw 'not developed').
fixed: Table: `[stickyHeader]` now pins the header inside a consumer’s OWN scroll container. The built-in container no longer traps the sticky header (new `--hub-table-container-overflow`, flipped to `visible` when `[stickyHeader]` is set), so a `max-height`/`overflow:auto` box you own keeps the header pinned.
fixed: Table: row/cell background is re-themeable by a plain `hub-table { --hub-table-bg: … }` tag selector (dark mode). The surface tokens are no longer declared on `:host` (which out-ranked the tag rule); each consumption site reads them as `var(--hub-table-bg, <default>)`, so a consumer’s tag rule reaches the cells with NO `!important`, and the sticky header stays opaque via the same fallback.
added: Table: optional selected-row accent bar — `--hub-table-selected-bar-width` (default `0`, no visual change) and `--hub-table-selected-bar-color`. With the `hub-table__row--selected` tint it gives the master-detail “active row” look (tint + leading bar) entirely through tokens and `[rowClass]`. RTL-aware.
added: Table: `[stickyHeader]` input pins the header while the body scrolls, decoupled from the built-in scroll frame — it works inside any consumer `max-height`/`overflow:auto` container. Overridable via `--hub-table-head-position` (default `sticky`).
added: Table: themeable header typography and padding — `--hub-table-head-font-size`, `--hub-table-head-font-weight`, `--hub-table-head-padding-x`, `--hub-table-head-padding-y` (surface/text already existed as `--hub-table-head-bg`/`--hub-table-head-color`). All default to existing values (no visual change).
changed: Table: the selected-row tint now also applies to a consumer-set `hub-table__row--selected` class, so a product can drive the selected look from its own state via `[rowClass]` instead of repainting the row.
added: Table: themeable header, row divider and scroll/sticky-header slots — `--hub-table-head-bg`/`--hub-table-head-color`, `--hub-table-row-divider-color`, and `--hub-table-container-max-block-size`/`--hub-table-head-sticky-top` (a fixed-height scroll body with `options.scrollable` now engages a sticky header). All default to existing values (no visual change).
added: Table: overridable row-expander caret icons via `--hub-table-icon-caret-up`/`--hub-table-icon-caret-down` (same var indirection as the sort icons).
added: List: opt-in item connector (timeline/pipeline) via the `connected` input, themed with `--hub-list-connector-color`/`-width`/`-style`/`-offset`. Default-off, list display only.
fixed: List (cards): long labels no longer overflow their card/grid track — `.hub-list__label` gets `min-width: 0` and wraps unbreakable tokens in cards mode.
added: Table: automatic client-side pagination. With a plain array in `[data]`, `paginate` true and no `totalItems`, the table searches, filters, sorts and slices the data entirely in memory and computes the total itself.
added: App-wide input defaults via `providePaginable({ defaults })` (`PaginableDefaults`): `paginate`, `perPage`, `perPageOptions`, `paginationPosition`, `paginationInfo`, `searchable`, `debounce`. Instance inputs still win; unset keys keep each component default.
changed: Table: the `paginate` input is now functional (previously inert) — it gates the client-side mode. Passing a `PaginationState` or setting `[totalItems]` keeps server mode. Set `[paginate]="false"` to render a full array unpaginated.
added: Agnostic form-controls integration: the table’s search input and rows-per-page select can be rendered by an external library via `provideHubPaginableFormControls()` and the `HUB_PAGINABLE_FORM_CONTROLS` token, with native `<input>` / `<select>` as the zero-dependency fallback.
added: Table: new `[loadingComponent]`, `[errorComponent]` and `[noResultsComponent]` inputs accept a `PaginableStateDefault` (eager component, lazy loader, or descriptor with input factory).
added: List: same `[loadingComponent]`, `[errorComponent]` and `[noResultsComponent]` inputs added to `hub-list`.
added: `providePaginable({ states })` — global app-level defaults for loading, error and no-results states shared across every paginable component.
added: `PaginableStateOutlet` internal outlet renders the resolved state component with the injected context inputs.
added: `PaginableLoadingDirective` (`loadingTpt`) and `PaginableErrorDirective` (`errorTpt`) exported for per-instance template-based state overrides.
changed: List: the BEM block now lives on the host element and the <ul> becomes .hub-list__items; backgrounds are controlled via --hub-list-bg (host) and --hub-list-item-bg (items).
added: List: new --hub-list-gap, --hub-list-items-bg and --hub-list-item-bg CSS variables.
fixed: The list card variables (--hub-list-cards-*) and --hub-paginator-font-size were documented but never applied; they now take effect. CSS variables reference fully synced (EN/ES).
removed: Breaking: renamed list --hub-list-container-* variables and removed the non-functional table --hub-table-breakpoint-* variables.
added: New CSS tokens for cell vertical alignment and icons
changed: Overlay utilities now live in ng-hub-ui-utils for shared dropdown support
fixed: Menu filter match mode labels and null match mode translations
added: Responsive table design for mobile devices
added: Row selection with checkboxes
changed: Performance optimizations for large tables
added: Basic pagination functionality
added: Column sorting capabilities
fixed: Table header alignment issues