Der @tanstack/angular-table Adapter ist ein Wrapper um die Kern-Tabellenlogik. Die meiste seiner Arbeit bezieht sich auf die Verwaltung des Zustands im "Angular Signals"-Stil, die Bereitstellung von Typen und die Rendering-Implementierung von Zell-/Kopfzeilen-/Fußzeilen-Vorlagen.
@tanstack/angular-table exportiert alle APIs von @tanstack/table-core und die folgenden
Akzeptiert eine Optionsfunktion oder einen berechneten Wert, der die Tabellenoptionen zurückgibt, und gibt eine Tabelle zurück.
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
Eine Angular-Strukturrichtlinie zum Rendern von Zell-/Kopfzeilen-/Fußzeilen-Vorlagen mit dynamischen Werten.
FlexRender unterstützt jede Art von Inhalt, die von Angular unterstützt wird
Sie können einfach die APIs cell.renderValue oder cell.getValue verwenden, um die Zellen Ihrer Tabelle zu rendern. Diese APIs geben jedoch nur die rohen Zellenwerte aus (von Accessor-Funktionen). Wenn Sie die Spaltendefinitionsoptionen cell: () => any verwenden, möchten Sie die FlexRenderDirective aus dem Adapter verwenden.
Die Zellspaltendefinition ist reaktiv und läuft in einem Injektionskontext. Dann können Sie Dienste injizieren oder Signale verwenden, um den gerenderten Inhalt automatisch zu ändern.
@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>
Um eine Komponente in eine bestimmte Spaltenkopfzeile/Zelle/Fußzeile zu rendern, können Sie eine FlexRenderComponent übergeben, die mit Ihrem `ComponentType` instanziiert ist, mit der Möglichkeit, Parameter wie Ein- und Ausgänge sowie einen benutzerdefinierten Injector einzubeziehen.
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
}
}
}
)
},
},
]
}
Im Hintergrund nutzt dies die ViewContainerRef#createComponent API. Daher sollten Sie Ihre benutzerdefinierten Eingaben mit dem @Input-Decorator oder Eingabe-/Modellsignalen deklarieren.
Sie können weiterhin auf den Tabellenzellkontext über die Funktion injectFlexRenderContext zugreifen, die den Kontextwert basierend auf den an die FlexRenderDirective übergebenen Props zurückgibt.
@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>>();
}
Alternativ können Sie eine Komponente in eine bestimmte Spaltenkopfzeile, Zelle oder Fußzeile rendern, indem Sie den Komponententyp an die entsprechenden Spaltendefinitionen übergeben. Diese Spaltendefinitionen werden zusammen mit dem context an die flexRender-Direktive übergeben.
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>
Eigenschaften des context, die in der flexRender-Direktive bereitgestellt werden, sind für Ihre Komponente zugänglich. Sie können die vom Ihrer Komponente benötigten Kontext-Eigenschaften explizit definieren. In diesem Beispiel ist der an flexRender übergebene Kontext vom Typ `HeaderContext`. Das Eingabesignal table, das eine Eigenschaft von `HeaderContext` ist, zusammen mit den Eigenschaften column und header, wird dann so definiert, dass es in der Komponente verwendet werden kann. Wenn eine der Kontext-Eigenschaften in Ihrer Komponente benötigt wird, können Sie sie gerne verwenden. Bitte beachten Sie, dass bei der Definition des Zugriffs auf Kontext-Eigenschaften mit diesem Ansatz nur Eingabesignale unterstützt werden.
@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>>()
}
Um einen `TemplateRef` in eine bestimmte Spaltenkopfzeile/Zelle/Fußzeile zu rendern, können Sie den `TemplateRef` in die Spaltendefinition übergeben.
Sie können auf die Daten des `TemplateRef` über die Eigenschaft $implicit zugreifen, die basierend auf dem Wert des `props`-Feldes von `flexRender` bestimmt wird.
In den meisten Fällen wird jeder `TemplateRef` mit dem `$implicit`-Kontext auf folgende Weise gerendert
<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>
Vollständiges Beispiel
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(),
},
]
}
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.