
Build Form
- 14 installs
- 293 repo stars
- Updated August 4, 2026
- sap/fundamental-ngx
Helps with ai & agent building tasks.
About
build-form is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- build-form
- AI & Agent Building
- AI-coding skill
Build Form by the numbers
- 14 all-time installs (skills.sh)
- Ranked #11,296 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sap/fundamental-ngx --skill build-formAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 14 |
|---|---|
| repo stars | ★ 293 |
| Last updated | August 4, 2026 |
| Repository | sap/fundamental-ngx ↗ |
What it does
Helps with ai & agent building tasks.
Files
Build Form: $ARGUMENTS
If $ARGUMENTS is empty, ask the user: (1) what the form is for, (2) which fields it needs, (3) whether validation is required.
Phase 1: Determine Scope
Parse from $ARGUMENTS or ask:
- Form name (PascalCase, e.g.,
UserProfile,ShippingAddress) - Fields: name, type (
text|number|select|date|checkbox|textarea), required (default) or optional (suffix with?, e.g.,birthDate:date?) - Layout:
1column |2columns (default) |3columns - Submit target: inline handler, emitted event to parent, or none
Phase 2: Gather Component Context
Call the @fundamental-ngx/mcp MCP server:
1. get_usage_guide('form') — composition order, fdp-form-group vs fd-form-item decision tree, pitfalls 2. get_component_api('fdp-form-group') — layout inputs, (onSubmit) output 3. get_component_api('fdp-form-field') — [id], [label], [required], [columns], hint/error slots 4. For each non-text field: get_component_api for its control (e.g., fdp-select, fdp-date-picker, fdp-checkbox)
If MCP is unavailable, read libs/mcp-server/src/data/usage-guides.ts.
Phase 3: Present Plan
Output this table before writing any code:
## Form Plan: [FormName]
**Layout:** 2 columns | **Validation trigger:** on submit + on touched
| Field | Control | Required | Validators |
|-------|---------|----------|------------|
| firstName | fdp-input | yes | minLength(2) |
| role | fdp-select | yes | required |
| birthDate | fdp-date-picker | no | — |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).
Also generate the fields interface at the top of the .component.ts file:
export interface [Name]Fields {
// one property per form field, e.g.:
firstName: string;
role: string;
}TypeScript (.component.ts)
Import each platform form component directly — they are all standalone. There is no barrel PlatformFormModule to import.
import { ChangeDetectionStrategy, Component, inject, output } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonModule } from '@fundamental-ngx/core/button';
import {
FormFieldComponent,
FormFieldErrorDirective,
FormGroupComponent,
InputComponent
// Add per-field: SelectComponent, TextAreaComponent, etc.
} from '@fundamental-ngx/platform/form';
// Import OptionItem only when using fdp-select:
// import { OptionItem } from '@fundamental-ngx/platform/shared';
@Component({
selector: 'app-[kebab-name]-form',
templateUrl: './[kebab-name]-form.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ReactiveFormsModule,
FormGroupComponent,
FormFieldComponent,
FormFieldErrorDirective, // always required when any field has validators
InputComponent,
// SelectComponent, TextAreaComponent, etc.
ButtonModule
]
})
export class [Name]FormComponent {
readonly submitted = output<[Name]Fields>();
private readonly _fb = inject(FormBuilder);
form: FormGroup = this._fb.group({
// fieldName: [defaultValue, [Validators.required, ...]]
});
onSubmit(): void {
if (this.form.valid) {
this.submitted.emit(this.form.getRawValue() as [Name]Fields);
}
}
}Standalone component imports reference:
| Template element | Import from @fundamental-ngx/platform/form |
|---|---|
fdp-form-group | FormGroupComponent |
fdp-form-field | FormFieldComponent |
fdp-input | InputComponent |
fdp-select | SelectComponent |
fdp-textarea | TextAreaComponent |
fdp-date-picker | PlatformDatePickerComponent |
fdp-checkbox | CheckboxComponent |
fdpFormFieldError (template directive) | FormFieldErrorDirective |
HTML Template (.component.html)
Wrap everything in a native <form> — FormGroupComponent does NOT render a <form> tag by default (useForm defaults to false) and has no footer slot. Submit/reset buttons must be placed outside <fdp-form-group> but inside the <form>.
Use columnLayout="XL2-L2-M2-S1" for a 2-column layout (not [layout]).
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<fdp-form-group [formGroup]="form" columnLayout="XL2-L2-M2-S1">
<!--
Error message templates — shared across ALL fields in this group.
Required whenever any fdp-form-field has [required]="true" or its
FormControl carries validators. Omitting these causes a runtime error:
"Validation strings are required for the any provided validations."
Add one <ng-template> per Angular error key your validators produce.
-->
<ng-template fdpFormFieldError="required">This field is required</ng-template>
<ng-template fdpFormFieldError="minlength" let-error
>Minimum {{ error.requiredLength }} characters required</ng-template
>
<ng-template fdpFormFieldError="email">Please enter a valid email address</ng-template>
<!-- add more for maxlength, pattern, custom validators, etc. -->
<fdp-form-field id="firstName" label="First Name" [required]="true" [column]="1">
<fdp-input name="firstName" [formControlName]="'firstName'"></fdp-input>
</fdp-form-field>
<!-- repeat fdp-form-field blocks for each field -->
</fdp-form-group>
<!-- Buttons go here, outside fdp-form-group but inside <form> -->
<div style="display: flex; gap: 0.5rem; margin-top: 1rem">
<button fd-button fdType="emphasized" type="submit">Submit</button>
<button fd-button fdType="transparent" type="button" (click)="form.reset()">Reset</button>
</div>
</form>`fdp-select` usage — use [list] with OptionItem[] from @fundamental-ngx/platform/shared. Each item's .value is what gets written to the FormControl:
<fdp-form-field id="role" label="Role" [required]="true" [column]="2">
<fdp-select name="role" [formControlName]="'role'" [list]="roleOptions"> </fdp-select>
</fdp-form-field>import { OptionItem } from '@fundamental-ngx/platform/shared';
readonly roleOptions: OptionItem[] = [
{ label: 'Developer', value: 'developer' },
{ label: 'Designer', value: 'designer' },
];SCSS (.component.scss)
Leave empty — form spacing and layout are handled by fundamental-styles. Only add custom rules if product requirements demand it.
Critical Rules
- `fdpFormFieldError` templates are mandatory when any field has validators — if
[required]="true"is set on anyfdp-form-field, or itsFormControlcarries validators, the component throws at runtime:"Validation strings are required for the any provided validations."Fix: add<ng-template fdpFormFieldError="required">...</ng-template>(and one per additional error key) as direct children offdp-form-group. They are shared automatically by all fields. ImportFormFieldErrorDirectivefrom@fundamental-ngx/platform/form. - One `<ng-template fdpFormFieldError>` per Angular error key — add templates for every key your validators can produce:
required,minlength,maxlength,email,pattern, and any custom keys. The template context (let-error) exposes the error value (e.g.error.requiredLengthforminlength). - Import standalone components directly — there is no
PlatformFormModule. Import each component individually:FormGroupComponent,FormFieldComponent,FormFieldErrorDirective,InputComponent,SelectComponent,TextAreaComponent, etc. - `fdp-form-field` not `fdp-form-item` —
fdp-form-itemis the corefd-API; platform forms usefdp-form-field - Wrap in `<form (ngSubmit)>`, NOT `(fdSubmit)` on `fdp-form-group` —
fdp-form-groupdoes not emitfdSubmit. Use a standard<form [formGroup]="form" (ngSubmit)="onSubmit()">wrapper. The component's own(onSubmit)output fires only when[useForm]="true"is set. - `columnLayout` (string), NOT `[layout]` (object) —
FormGroupComponentacceptscolumnLayout="XL2-L2-M2-S1", not[layout]="{ columns: 2 }". The format isXLn-Ln-Mn-Sn. - No `[fdpFormFooter]` directive — it does not exist. Place buttons outside
<fdp-form-group>but inside your<form>wrapper. - `[list]` (not `[options]`) on `fdp-select` — pass
OptionItem[]to[list]. Do NOT use[options],displayKey, orvalueKey— these inputs do not exist onSelectComponent. - `[id]` on `fdp-form-field` is required — it sets the
foron the label and theidon the inner control via content projection; omitting it breaks label–control association and accessibility - `name` attribute on every control — must be unique within the form; matches the
FormControlName - `[column]` (singular) not `[columns]` —
fdp-form-fielduses[column]to set which column it occupies in the grid;[columns]is the span count - Do NOT add
standalone: true— default since Angular 19 - Do NOT use
ngClassorngStyle— use direct class/style bindings
Phase 5: Validate
nx run <project>:build # or: ng buildFix any TypeScript type errors or missing import errors before reporting done.
Output
## Build Form: [FormName]
**Files generated:**
- src/app/.../[kebab-name]-form.component.ts
- src/app/.../[kebab-name]-form.component.html
- src/app/.../[kebab-name]-form.component.scss
**Imports required in parent:**
- `import { [Name]FormComponent } from './[kebab-name]-form/[kebab-name]-form.component'`
**Next steps:**
- [ ] Add [Name]FormComponent to the parent's imports array
- [ ] Handle the (submitted) output event in the parent
- [ ] Customize per-field error messages with fdp-form-message if default messages are insufficient
- [ ] For multi-step forms, wrap multiple fdp-form-group instances in a wizard container