Build Table: $ARGUMENTS
SkillDev toolsBuild a @fundamental-ngx/platform data table with FdpTableDataSource, sorting, filtering, pagination, and row selection
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Build Table: $ARGUMENTS skill
What this skill tells your AI
The instructions your AI receives, as published by sap/fundamental-ngx in .claude/skills/build-table/SKILL.md and read by ahel’s review.
If $ARGUMENTS is empty, ask: (1) what data the table displays, (2) which features are needed.
Phase 1: Determine Scope
Parse from $ARGUMENTS or ask:
- Table name (PascalCase, e.g.,
Users,Orders) - Data model: field names + TypeScript types (used for column definitions and the data interface). If not provided in
$ARGUMENTS, ask explicitly: "What columns should the table have? Please list field name and type, e.g.:id:number, customerName:string, status:string" - Base component:
fdp-table(platform, feature-rich — default) |fd-table(core, markup-level, use only for purely presentational tables) - Features (check all that apply):
sort— sortable column headersfilter— per-column or global search filterpaginate— page size picker + page navigationselect— row selection (single|multiple)toolbar— title bar with action buttons
- Data source: static array | observable | HTTP service (lazy)
Default to fdp-table with sort + paginate unless the user specifies otherwise.
Phase 2: Gather Component Context
Call the @fundamental-ngx/mcp MCP server:
get_usage_guide('table')— DataSource patterns, variant decision tree, pitfallsget_component_api('fdp-table')— all inputs/outputs, selection mode options, page size inputs- If
filterrequested:get_component_api('fdp-table-toolbar')for filter toolbar wiring - If custom cell rendering needed: look for
fdpTableCelltemplate directive API
If MCP is unavailable, read libs/mcp-server/src/data/usage-guides.ts.
Phase 3: Present Plan
Output this summary before writing any code:
## Table Plan: [TableName]
**Component:** fdp-table
**DataSource type:** FdpTableDataSource (wraps observable)
**Features:** sort ✓ | filter ✓ | paginate ✓ | select(multiple) ✓
| Column key | Label | Type | Sortable | Filterable | Width |
|------------|-------|------|----------|------------|-------|
| name | Name | string | yes | yes | auto |
| status | Status | string | yes | yes | 120px |
| createdAt | Created | date | yes | no | 160px |
**Toolbar actions:** Add, Delete selected
Stop here and wait for approval before generating code.
Phase 4: Generate Component
Create three files in the target path (ask the user if not already known).
TypeScript (.component.ts)
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import {
TableComponent,
TableColumnComponent,
TableToolbarComponent,
TableToolbarActionsComponent,
TableViewSettingsDialogComponent
} from '@fundamental-ngx/platform/table';
// FdpTableDataSource is a type alias only — use the concrete class from table-helpers:
import {
ArrayTableDataSource, // for static arrays
ObservableTableDataSource, // for observables
TableInitialStateDirective, // REQUIRED — initializes state.columns from column definitions
TableRowSelectionChangeEvent
} from '@fundamental-ngx/platform/table-helpers';
// NOTE: PlatformTableModule is deprecated — always import individual components above.
export interface [Name]Row {
// map data model fields here
id: number;
}
@Component({
selector: 'app-[kebab-name]-table',
templateUrl: './[kebab-name]-table.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TableComponent, TableColumnComponent, TableToolbarComponent, TableToolbarActionsComponent, TableViewSettingsDialogComponent,
TableInitialStateDirective]
})
export class [Name]TableComponent {
readonly dataSource = new ArrayTableDataSource<[Name]Row>(this._getData());
readonly selectedRows = signal<[Name]Row[]>([]);
onRowSelectionChange(event: TableRowSelectionChangeEvent<[Name]Row>): void {
this.selectedRows.set(event.selection);
}
add(): void {
// TODO: open a dialog or navigate to an add form
}
deleteSelected(): void {
// TODO: call your service to delete this.selectedRows()
this.selectedRows.set([]);
}
private _getData(): [Name]Row[] {
// Replace with your observable or HTTP call:
// return this._service.getItems();
return [];
}
}
HTML Template (.component.html)
<fdp-table
[dataSource]="dataSource"
selectionMode="multiple"
[pageSize]="25"
(rowSelectionChange)="onRowSelectionChange($event)"
>
<fdp-table-toolbar title="[TableName]" [hideItemCount]="false">
<fdp-table-toolbar-actions>
<button fd-button fdType="emphasized" (click)="add()">Add</button>
@if (selectedRows().length > 0) {
<button fd-button fdType="negative" (click)="deleteSelected()">Delete ({{ selectedRows().length }})</button>
}
</fdp-table-toolbar-actions>
</fdp-table-toolbar>
<fdp-column name="name" key="name" label="Name" [sortable]="true" [filterable]="true"> </fdp-column>
<!-- repeat fdp-column for each field -->
<fdp-table-view-settings-dialog></fdp-table-view-settings-dialog>
</fdp-table>
SCSS (.component.scss)
Leave empty — table layout is handled by fundamental-styles. Add rules only for host-level sizing (e.g., height: 100%).
DataSource Patterns
Choose based on data origin:
| Source | Pattern |
|---|---|
| Static array | new ArrayTableDataSource(myArray) — from @fundamental-ngx/platform/table-helpers |
| Observable | new ObservableTableDataSource(myObservable$) — from @fundamental-ngx/platform/table-helpers |
| HTTP (lazy, server-side sort/filter) | Implement TableDataSource<T> interface — override fetch(tableState) |
For server-side sorting/filtering, implement TableDataSource<T>:
export class [Name]DataSource extends TableDataSource<[Name]Row> {
fetch(tableState?: TableState): Observable<[Name]Row[]> {
const { sortBy, filterBy, page } = tableState ?? {};
return this._service.query({ sortBy, filterBy, page });
}
}
Critical Rules
TableInitialStateDirectiveis required — import it from@fundamental-ngx/platform/table-helpersand add it to the component'simportsarray. Its selector matchesfdp-tableand it initializesstate.columnsfrom the column definitions before the first render. Without it,state.columnsstays[]and the table shows "Right now, there are no visible columns." even though thefdp-columnelements are correctly declared.- Import individual components, not
PlatformTableModule—PlatformTableModuleis deprecated and cannot be statically resolved in a standalone component'simportsarray. Always importTableComponent,TableColumnComponent,TableToolbarComponent,TableToolbarActionsComponent, andTableP13DialogComponentdirectly from@fundamental-ngx/platform/table. FdpTableDataSourceis a type alias, not a class — it lives in@fundamental-ngx/platform/table-helpersand is exported underexport type. UseArrayTableDataSourcefor static arrays orObservableTableDataSourcefor observables; both are in@fundamental-ngx/platform/table-helpers.TableRowSelectionChangeEventis fromtable-helpers— import it from@fundamental-ngx/platform/table-helpers, not@fundamental-ngx/platform/table.- There are no
fdpTableSortable/fdpTableFilterabledirectives — sort and filter are activated solely by[sortable]="true"and[filterable]="true"on eachfdp-column. No extra directive goes on<fdp-table>. [pageSizeOptions]does not exist —[pageSize]is the only pagination input on<fdp-table>. There is no per-page picker input.fdp-table-view-settings-dialogis required — without it, the column personalization panel and the sort/filter apply buttons do not render; include it inside<fdp-table>even when not explicitly requestednameandkeyare both required onfdp-column—nameis the unique identifier (camelCase),keymaps to the data row's property path; they can be identical- Do NOT pass a
BehaviorSubjectdirectly — wrap it:new ObservableTableDataSource(subject.asObservable()); passing a subject directly causes double-subscription issues selectionModeis a string input — values:'single'|'multiple'|'none'; do not use[selectionMode]="SelectionMode.Multiple"enum binding unless you import the enum- Bundle budget — adding
@fundamental-ngx/platformincreases the initial bundle to ~4.6 MB. In a standalone app raise the budget inangular.jsontomaximumWarning: "5MB", maximumError: "8MB". - Custom cell templates use
fdpTableCellstructural directive —<ng-template fdpTableCell let-row>{{ row.date | date }}</ng-template>inside thefdp-column - Do NOT add
standalone: true— default since Angular 19 - Do NOT use
*ngIf/*ngForin templates — use@if/@for
Phase 5: Validate
# NX monorepo
nx run <project>:build
# Standalone Angular CLI app
ng build # or: npm run build
Replace the empty _getData() return with at least a small static array and confirm the table renders with column headers before reporting done.
Output
## Build Table: [TableName]
**Files generated:**
- src/app/.../[kebab-name]-table.component.ts
- src/app/.../[kebab-name]-table.component.html
- src/app/.../[kebab-name]-table.component.scss
**Imports required in parent:**
- `import { [Name]TableComponent } from './[kebab-name]-table/[kebab-name]-table.component'`
- Note: `TableInitialStateDirective` is declared inside the generated component — no additional parent import needed
**Features implemented:** sort ✓ | filter ✓ | paginate(25/page) ✓ | select(multiple) ✓
**Next steps:**
- [ ] Replace static array in _getData() with your real service call
- [ ] Add custom cell templates (fdpTableCell) for date/currency/status columns
- [ ] For >10k rows: implement server-side TableDataSource with fetch() method
- [ ] Add row-level action column if inline edit/delete is needed
Signals
- GitHub stars
- 294
- Forks
- 147
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
build-table- Source
- github.com/sap/fundamental-ngx