Skip to content

Commit 2c1725b

Browse files
committed
clean docs
test fix doc add flexRenderComponent util
1 parent b99169a commit 2c1725b

File tree

9 files changed

+258
-128
lines changed

9 files changed

+258
-128
lines changed

docs/framework/angular/angular-table.md

Lines changed: 145 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -40,41 +40,171 @@ FlexRender supports any type of content supported by Angular:
4040
- A [TemplateRef](https://angular.dev/api/core/TemplateRef)
4141
- A [Component](https://angular.dev/api/core/Component) wrapped into `FlexRenderComponent`
4242

43-
Example:
43+
You can just use the `cell.renderValue` or `cell.getValue` APIs to render the cells of your table. However,
44+
these APIs will only spit out the raw cell values (from accessor functions).
45+
If you are using the `cell: () => any` column definition options, you will want to use the `FlexRenderDirective` from the adapter.
46+
47+
Cell column definition is **reactive** and runs into an **injection context**, then you can inject services or make use of signals to automatically modify the rendered content.
48+
49+
#### Example
4450

4551
```ts
4652
@Component({
4753
imports: [FlexRenderDirective],
4854
//...
4955
})
56+
class YourComponent {}
5057
```
5158

5259
```angular-html
5360
5461
<tbody>
5562
@for (row of table.getRowModel().rows; track row.id) {
56-
<tr>
57-
@for (cell of row.getVisibleCells(); track cell.id) {
58-
<td>
59-
<ng-container
60-
*flexRender="
63+
<tr>
64+
@for (cell of row.getVisibleCells(); track cell.id) {
65+
<td>
66+
<ng-container
67+
*flexRender="
6168
cell.column.columnDef.cell;
6269
props: cell.getContext();
6370
let cell
6471
"
65-
>
66-
<!-- if you want to render a simple string -->
67-
{{ cell }}
68-
<!-- if you want to render an html string -->
69-
<div [innerHTML]="cell"></div>
70-
</ng-container>
71-
</td>
72-
}
73-
</tr>
72+
>
73+
<!-- if you want to render a simple string -->
74+
{{ cell }}
75+
<!-- if you want to render an html string -->
76+
<div [innerHTML]="cell"></div>
77+
</ng-container>
78+
</td>
79+
}
80+
</tr>
7481
}
7582
</tbody>
7683
```
7784

85+
#### Rendering a Component
86+
87+
To render a Component into a specific column header/cell/footer, you can pass a `FlexRenderComponent` instantiated with
88+
your `ComponentType, with the ability to include parameters such as inputs and an injector.
89+
90+
```ts
91+
import {flexRenderComponent} from "./flex-render-component";
92+
import {ChangeDetectionStrategy} from "@angular/core";
93+
94+
@Component({
95+
template: `
96+
...
97+
`,
98+
standalone: true,
99+
changeDetectionStrategy: ChangeDetectionStrategy.OnPush
100+
})
101+
class CustomCell {
102+
readonly content = input.required<string>();
103+
104+
readonly type = input<MyType>()
105+
}
106+
107+
class AppComponent {
108+
columns: ColumnDef<unknown>[] = [
109+
{
110+
id: 'custom-cell',
111+
header: () => {
112+
const translateService = inject(TranslateService);
113+
return translateService.translate('...');
114+
},
115+
cell: (context) => {
116+
return flexRenderComponent(
117+
MyCustomComponent,
118+
{
119+
injector, // Optional injector
120+
inputs: {
121+
// Mandatory input since we are using `input.required()
122+
content: context.row.original.rowProperty,
123+
type // Optional input
124+
}
125+
}
126+
)
127+
},
128+
},
129+
]
130+
}
131+
```
132+
133+
Underneath, this utilizes
134+
the [ViewContainerRef#createComponent](https://angular.dev/api/core/ViewContainerRef#createComponent) api.
135+
Therefore, you should declare your custom inputs using the @Input decorator or input/model signals.
136+
137+
You can still access the table cell context through the `injectFlexRenderContext` function, which returns the context
138+
value based on the props you pass to the `FlexRenderDirective`.
139+
140+
```ts
141+
142+
@Component({
143+
// ...
144+
})
145+
class CustomCellComponent {
146+
// context of a cell component
147+
readonly context = injectFlexRenderContext<CellContext<TData, TValue>>();
148+
// context of a header/footer component
149+
readonly context = injectFlexRenderContext<HeaderContext<TData, TValue>>();
150+
}
151+
```
152+
153+
Alternatively, you can render a component into a specific column header, cell, or footer by passing the component type
154+
to the corresponding column definitions. These column definitions will be provided to the `flexRender` directive along
155+
with the `context`.
156+
157+
```ts
158+
class AppComponent {
159+
columns: ColumnDef<Person>[] = [
160+
{
161+
id: 'select',
162+
header: () => TableHeadSelectionComponent<Person>,
163+
cell: () => TableRowSelectionComponent<Person>,
164+
},
165+
]
166+
}
167+
```
168+
169+
```angular-html
170+
<ng-container
171+
*flexRender="
172+
header.column.columnDef.header;
173+
props: header.getContext();
174+
let headerCell
175+
"
176+
>
177+
{{ headerCell }}
178+
</ng-container>
179+
```
180+
181+
Properties of `context` provided in the `flexRender` directive will be accessible to your component.
182+
You can explicitly define the context properties required by your component.
183+
In this example, the context provided to flexRender is of type HeaderContext.
184+
Input signal `table`, which is a property of HeaderContext together with `column` and `header` properties,
185+
is then defined to be used in the component. If any of the context properties are
186+
needed in your component, feel free to use them. Please take note that only input signal is supported,
187+
when defining access to context properties, using this approach.
188+
189+
```angular-ts
190+
@Component({
191+
template: `
192+
<input
193+
type="checkbox"
194+
[checked]="table().getIsAllRowsSelected()"
195+
[indeterminate]="table().getIsSomeRowsSelected()"
196+
(change)="table().toggleAllRowsSelected()"
197+
/>
198+
`,
199+
// ...
200+
})
201+
export class TableHeadSelectionComponent<T> {
202+
//column = input.required<Column<T, unknown>>()
203+
//header = input.required<Header<T, unknown>>()
204+
table = input.required<Table<T>>()
205+
}
206+
```
207+
78208
#### Rendering a TemplateRef
79209

80210
In order to render a TemplateRef into a specific column header/cell/footer, you can pass the TemplateRef into the column
@@ -171,101 +301,3 @@ class AppComponent {
171301
]
172302
}
173303
```
174-
175-
#### Rendering a Component
176-
177-
To render a Component into a specific column header/cell/footer, you can pass a `FlexRenderComponent instantiated with
178-
your `ComponentType, with the ability to include optional parameters such as inputs and an injector.
179-
180-
```ts
181-
import {FlexRenderComponent} from "@tanstack/angular-table";
182-
183-
class AppComponent {
184-
columns: ColumnDef<unknown>[] = [
185-
{
186-
id: 'customCell',
187-
header: () => new FlexRenderComponent(
188-
CustomCellComponent,
189-
{}, // optional inputs
190-
injector // optional injector
191-
),
192-
cell: () => this.customCell(),
193-
},
194-
]
195-
}
196-
```
197-
198-
Underneath, this utilizes
199-
the [ViewContainerRef#createComponent](https://angular.dev/api/core/ViewContainerRef#createComponent) api.
200-
Therefore, you should declare your custom inputs using the @Input decorator or input/model signals.
201-
202-
You can still access the table cell context through the `injectFlexRenderContext` function, which returns the context
203-
value based on the props you pass to the `FlexRenderDirective`.
204-
205-
```ts
206-
@Component({
207-
// ...
208-
})
209-
class CustomCellComponent {
210-
// context of a cell component
211-
readonly context = injectFlexRenderContext<CellContext<TData, TValue>>();
212-
// context of a header/footer component
213-
readonly context = injectFlexRenderContext<HeaderContext<TData, TValue>>();
214-
}
215-
```
216-
217-
Alternatively, you can render a component into a specific column header, cell, or footer by passing the component type
218-
to the corresponding column definitions. These column definitions will be provided to the `flexRender` directive along with the `context`.
219-
220-
```ts
221-
import {FlexRenderComponent} from "@tanstack/angular-table";
222-
223-
class AppComponent {
224-
columns: ColumnDef<Person>[] = [
225-
{
226-
id: 'select',
227-
header: () => TableHeadSelectionComponent<Person>,
228-
cell: () => TableRowSelectionComponent<Person>,
229-
},
230-
]
231-
}
232-
```
233-
234-
```angular2html
235-
<ng-container
236-
*flexRender="
237-
header.column.columnDef.header;
238-
props: header.getContext();
239-
let headerCell
240-
"
241-
>
242-
{{ headerCell }}
243-
</ng-container>
244-
```
245-
246-
Properties of `context` provided in the `flexRender` directive will be accessible to your component.
247-
You can explicitly define the context properties required by your component.
248-
In this example, the context provided to flexRender is of type HeaderContext.
249-
Input signal `table`, which is a property of HeaderContext together with `column` and `header` properties,
250-
is then defined to be used in the component. If any of the context properties are
251-
needed in your component, feel free to use them. Please take note that only input signal is supported,
252-
when defining access to context properties, using this approach.
253-
254-
```angular-ts
255-
@Component({
256-
template: `
257-
<input
258-
type="checkbox"
259-
[checked]="table().getIsAllRowsSelected()"
260-
[indeterminate]="table().getIsSomeRowsSelected()"
261-
(change)="table().toggleAllRowsSelected()"
262-
/>
263-
`,
264-
// ...
265-
})
266-
export class TableHeadSelectionComponent<T> {
267-
//column = input.required<Column<T, unknown>>()
268-
//header = input.required<Header<T, unknown>>()
269-
table = input.required<Table<T>>()
270-
}
271-
```

examples/angular/row-selection/src/app/app.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import {
1010
ColumnDef,
1111
createAngularTable,
12-
FlexRenderComponent,
12+
flexRenderComponent,
1313
FlexRenderDirective,
1414
getCoreRowModel,
1515
getFilteredRowModel,
@@ -43,10 +43,10 @@ export class AppComponent {
4343
{
4444
id: 'select',
4545
header: () => {
46-
return new FlexRenderComponent(TableHeadSelectionComponent)
46+
return flexRenderComponent(TableHeadSelectionComponent)
4747
},
4848
cell: () => {
49-
return new FlexRenderComponent(TableRowSelectionComponent)
49+
return flexRenderComponent(TableRowSelectionComponent)
5050
},
5151
},
5252
{

packages/angular-table/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
],
4444
"scripts": {
4545
"clean": "rimraf ./build",
46-
"test:types": "tsc --noEmit",
46+
"test:types": "tsc --noEmit && vitest --typecheck",
4747
"test:lib": "vitest",
4848
"test:lib:dev": "vitest --watch",
4949
"build": "ng-packagr -p ng-package.json -c tsconfig.build.json && rimraf ./build/lib/package.json"

packages/angular-table/src/flex-render.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import {
1717
} from '@angular/core'
1818
import { FlexRenderComponentProps } from './flex-render/context'
1919
import { FlexRenderFlags } from './flex-render/flags'
20-
import { FlexRenderComponent } from './flex-render/flex-render-component'
20+
import {
21+
flexRenderComponent,
22+
FlexRenderComponent,
23+
} from './flex-render/flex-render-component'
2124
import { FlexRenderComponentFactory } from './flex-render/flex-render-component-ref'
2225
import {
2326
FlexRenderComponentView,
@@ -273,7 +276,10 @@ export class FlexRenderDirective<TProps extends NonNullable<unknown>>
273276
component: Extract<FlexRenderTypedContent, { kind: 'component' }>
274277
): FlexRenderComponentView {
275278
const view = this.#flexRenderComponentFactory.createComponent(
276-
new FlexRenderComponent(component.content, this.props),
279+
flexRenderComponent(component.content, {
280+
inputs: this.props,
281+
injector: this.injector,
282+
}),
277283
this.injector
278284
)
279285
view.setInputs({ ...this.props })

0 commit comments

Comments
 (0)