Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
sap avatar

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-form

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs14
repo stars293
Last updatedAugust 4, 2026
Repositorysap/fundamental-ngx

What it does

Helps with ai & agent building tasks.

Files

SKILL.mdMarkdownGitHub ↗

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: 1 column | 2 columns (default) | 3 columns
  • 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 elementImport from @fundamental-ngx/platform/form
fdp-form-groupFormGroupComponent
fdp-form-fieldFormFieldComponent
fdp-inputInputComponent
fdp-selectSelectComponent
fdp-textareaTextAreaComponent
fdp-date-pickerPlatformDatePickerComponent
fdp-checkboxCheckboxComponent
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 any fdp-form-field, or its FormControl carries 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 of fdp-form-group. They are shared automatically by all fields. Import FormFieldErrorDirective from @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.requiredLength for minlength).
  • 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-item is the core fd- API; platform forms use fdp-form-field
  • Wrap in `<form (ngSubmit)>`, NOT `(fdSubmit)` on `fdp-form-group`fdp-form-group does not emit fdSubmit. 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)FormGroupComponent accepts columnLayout="XL2-L2-M2-S1", not [layout]="{ columns: 2 }". The format is XLn-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, or valueKey — these inputs do not exist on SelectComponent.
  • `[id]` on `fdp-form-field` is required — it sets the for on the label and the id on 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-field uses [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 ngClass or ngStyle — use direct class/style bindings

Phase 5: Validate

nx run <project>:build   # or: ng build

Fix 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

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.