`@tanstack/angular-table` 适配器是核心表格逻辑的包装器。它的主要工作是管理“Angular Signals”方式的状态,提供类型以及单元格/表头/表尾模板的渲染实现。
`@tanstack/angular-table` 重新导出了 `@tanstack/table-core` 的所有 API 以及以下内容:
接受一个返回表格选项的选项函数或计算值,并返回一个表格。
import {createAngularTable} from '@tanstack/angular-table'
export class AppComponent {
data = signal<Person[]>([])
table = createAngularTable(() => ({
data: this.data(),
columns: defaultColumns,
getCoreRowModel: getCoreRowModel(),
}))
}
// ...render your table in template
import {createAngularTable} from '@tanstack/angular-table'
export class AppComponent {
data = signal<Person[]>([])
table = createAngularTable(() => ({
data: this.data(),
columns: defaultColumns,
getCoreRowModel: getCoreRowModel(),
}))
}
// ...render your table in template
一个 Angular 结构指令,用于使用动态值渲染单元格/表头/表尾模板。
FlexRender 支持 Angular 支持的任何类型的内容
您可以使用 `cell.renderValue` 或 `cell.getValue` API 来渲染表格的单元格。然而,这些 API 只会输出原始的单元格值(来自访问器函数)。如果您使用的是 `cell: () => any` 列定义选项,您将需要使用适配器中的 `FlexRenderDirective`。
单元格列定义是响应式的,并且运行在一个注入上下文中,然后您可以注入服务或使用信号来自动修改渲染的内容。
@Component({
imports: [FlexRenderDirective],
//...
})
class YourComponent {}
@Component({
imports: [FlexRenderDirective],
//...
})
class YourComponent {}
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext();
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
</td>
}
</tr>
}
</tbody>
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext();
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
</td>
}
</tr>
}
</tbody>
要将组件渲染到特定的列标题/单元格/表尾,您可以传递一个实例化了您的 `ComponentType` 的 `FlexRenderComponent`,并能够包含输入、输出和自定义注入器等参数。
import {flexRenderComponent} from "./flex-render-component";
import {ChangeDetectionStrategy, input, output} from "@angular/core";
@Component({
template: `
...
`,
standalone: true,
changeDetectionStrategy: ChangeDetectionStrategy.OnPush,
host: {
'(click)': 'clickEvent.emit($event)'
}
})
class CustomCell {
readonly content = input.required<string>();
readonly cellType = input<MyType>();
// An output that will emit for every cell click
readonly clickEvent = output<Event>();
}
class AppComponent {
columns: ColumnDef<unknown>[] = [
{
id: 'custom-cell',
header: () => {
const translateService = inject(TranslateService);
return translateService.translate('...');
},
cell: (context) => {
return flexRenderComponent(
MyCustomComponent,
{
injector, // Optional injector
inputs: {
// Mandatory input since we are using `input.required()
content: context.row.original.rowProperty,
// cellType? - Optional input
},
outputs: {
clickEvent: () => {
// Do something
}
}
}
)
},
},
]
}
import {flexRenderComponent} from "./flex-render-component";
import {ChangeDetectionStrategy, input, output} from "@angular/core";
@Component({
template: `
...
`,
standalone: true,
changeDetectionStrategy: ChangeDetectionStrategy.OnPush,
host: {
'(click)': 'clickEvent.emit($event)'
}
})
class CustomCell {
readonly content = input.required<string>();
readonly cellType = input<MyType>();
// An output that will emit for every cell click
readonly clickEvent = output<Event>();
}
class AppComponent {
columns: ColumnDef<unknown>[] = [
{
id: 'custom-cell',
header: () => {
const translateService = inject(TranslateService);
return translateService.translate('...');
},
cell: (context) => {
return flexRenderComponent(
MyCustomComponent,
{
injector, // Optional injector
inputs: {
// Mandatory input since we are using `input.required()
content: context.row.original.rowProperty,
// cellType? - Optional input
},
outputs: {
clickEvent: () => {
// Do something
}
}
}
)
},
},
]
}
底层利用了 `ViewContainerRef#createComponent` API。因此,您应该使用 @Input 装饰器或 input/model 信号来声明您的自定义输入。
您仍然可以通过 `injectFlexRenderContext` 函数访问表格单元格上下文,该函数根据您传递给 `FlexRenderDirective` 的 props 返回上下文值。
@Component({
// ...
})
class CustomCellComponent {
// context of a cell component
readonly context = injectFlexRenderContext<CellContext<TData, TValue>>();
// context of a header/footer component
readonly context = injectFlexRenderContext<HeaderContext<TData, TValue>>();
}
@Component({
// ...
})
class CustomCellComponent {
// context of a cell component
readonly context = injectFlexRenderContext<CellContext<TData, TValue>>();
// context of a header/footer component
readonly context = injectFlexRenderContext<HeaderContext<TData, TValue>>();
}
或者,您可以通过将组件类型传递给相应的列定义来将组件渲染到特定的列标题、单元格或表尾。这些列定义将与 `context` 一起提供给 `flexRender` 指令。
class AppComponent {
columns: ColumnDef<Person>[] = [
{
id: 'select',
header: () => TableHeadSelectionComponent<Person>,
cell: () => TableRowSelectionComponent<Person>,
},
]
}
class AppComponent {
columns: ColumnDef<Person>[] = [
{
id: 'select',
header: () => TableHeadSelectionComponent<Person>,
cell: () => TableRowSelectionComponent<Person>,
},
]
}
<ng-container
*flexRender="
header.column.columnDef.header;
props: header.getContext();
let headerCell
"
>
{{ headerCell }}
</ng-container>
<ng-container
*flexRender="
header.column.columnDef.header;
props: header.getContext();
let headerCell
"
>
{{ headerCell }}
</ng-container>
`flexRender` 指令中提供的 `context` 的属性将可以在您的组件中使用。您可以显式定义组件所需的 context 属性。在此示例中,传递给 flexRender 的 context 是 HeaderContext 类型。然后定义 HeaderContext 的属性 `table`(一个输入信号)、`column` 和 `header` 属性,以便在组件中使用。如果您的组件需要任何 context 属性,请随时使用它们。请注意,在定义访问 context 属性时,使用此方法仅支持输入信号。
@Component({
template: `
<input
type="checkbox"
[checked]="table().getIsAllRowsSelected()"
[indeterminate]="table().getIsSomeRowsSelected()"
(change)="table().toggleAllRowsSelected()"
/>
`,
// ...
})
export class TableHeadSelectionComponent<T> {
//column = input.required<Column<T, unknown>>()
//header = input.required<Header<T, unknown>>()
table = input.required<Table<T>>()
}
@Component({
template: `
<input
type="checkbox"
[checked]="table().getIsAllRowsSelected()"
[indeterminate]="table().getIsSomeRowsSelected()"
(change)="table().toggleAllRowsSelected()"
/>
`,
// ...
})
export class TableHeadSelectionComponent<T> {
//column = input.required<Column<T, unknown>>()
//header = input.required<Header<T, unknown>>()
table = input.required<Table<T>>()
}
为了将 TemplateRef 渲染到特定的列标题/单元格/表尾,您可以将 TemplateRef 传递到列定义中。
您可以通过 `$implicit` 属性访问 TemplateRef 数据,该属性的值取决于传递给 flexRender 的 props 字段中的内容。
在大多数情况下,每个 TemplateRef 都将以这种方式使用 $implicit context 来渲染,具体取决于单元格类型:
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext();
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
<ng-template #myCell let-context>
<!-- render something with context -->
</ng-template>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext();
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
<ng-template #myCell let-context>
<!-- render something with context -->
</ng-template>
完整示例
import type {
CellContext,
ColumnDef,
HeaderContext,
} from '@tanstack/angular-table'
import {Component, TemplateRef, viewChild} from '@angular/core'
@Component({
template: `
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext(); // Data given to the TemplateRef
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
</td>
}
</tr>
}
</tbody>
<ng-template #customHeader let-context>
{{ context.getValue() }}
</ng-template>
<ng-template #customCell let-context>
{{ context.getValue() }}
</ng-template>
`,
})
class AppComponent {
customHeader =
viewChild.required<TemplateRef<{ $implicit: HeaderContext<any, any> }>>(
'customHeader'
)
customCell =
viewChild.required<TemplateRef<{ $implicit: CellContext<any, any> }>>(
'customCell'
)
columns: ColumnDef<unknown>[] = [
{
id: 'customCell',
header: () => this.customHeader(),
cell: () => this.customCell(),
},
]
}
import type {
CellContext,
ColumnDef,
HeaderContext,
} from '@tanstack/angular-table'
import {Component, TemplateRef, viewChild} from '@angular/core'
@Component({
template: `
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container
*flexRender="
cell.column.columnDef.cell;
props: cell.getContext(); // Data given to the TemplateRef
let cell
"
>
<!-- if you want to render a simple string -->
{{ cell }}
<!-- if you want to render an html string -->
<div [innerHTML]="cell"></div>
</ng-container>
</td>
}
</tr>
}
</tbody>
<ng-template #customHeader let-context>
{{ context.getValue() }}
</ng-template>
<ng-template #customCell let-context>
{{ context.getValue() }}
</ng-template>
`,
})
class AppComponent {
customHeader =
viewChild.required<TemplateRef<{ $implicit: HeaderContext<any, any> }>>(
'customHeader'
)
customCell =
viewChild.required<TemplateRef<{ $implicit: CellContext<any, any> }>>(
'customCell'
)
columns: ColumnDef<unknown>[] = [
{
id: 'customCell',
header: () => this.customHeader(),
cell: () => this.customCell(),
},
]
}
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。