Skip to content
Back to skills

Ng Forms

ASecurity

Build signal-based forms in Angular v22+ using the Signal Forms API (public API since v22, imported from @angular/forms/signals). Use for form creation with automatic two-way binding, schema-based validation, field state management, and dynamic forms. Triggers on form implementation, adding validation, creating multi-step forms, or building forms with conditional fields. Don't use for template-driven forms without signals or third-party form libraries like Formly or ngx-formly.

  • 13 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
ai-agentstypescriptreactangularapi

Works with

  • api

Security analysis

A100/100

Pro scans all 7 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add L-X-T/ng-agentic-skills --skill ng-forms --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ng Forms?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ng Forms
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/l-x-t-ng-forms/badge)](https://www.skillsdirectory.com/skills/l-x-t-ng-forms)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ng-forms
description: Build signal-based forms in Angular v22+ using the Signal Forms API (public API since v22, imported from @angular/forms/signals). Use for form creation with automatic two-way binding, schema-based validation, field state management, and dynamic forms. Triggers on form implementation, adding validation, creating multi-step forms, or building forms with conditional fields. Don't use for template-driven forms without signals or third-party form libraries like Formly or ngx-formly.
license: MIT
metadata:
  author: Alexander Thalhammer
  version: '1.0'
---

# Angular Signal Forms

Build type-safe, reactive forms with Angular's Signal Forms API: a signal model is the single
source of truth, the form structure is derived from it, and a schema function declares validation
and field state. Signal Forms are public API in Angular v22 (imported from `@angular/forms/signals`).

For exhaustive API depth, read the angular-developer skill's canonical reference at
[../angular-developer/references/signal-forms.md](../angular-developer/references/signal-forms.md)
(vendored from Angular's official skill and corrected locally to the stable v22 API – see the local adaptations in `02-SKILLS.md` before re-syncing it). The references below are the project-shaped fast path.

## End-to-end example

```typescript
import { Component, inject, signal } from '@angular/core';
import { form, FormField, required, email, submit } from '@angular/forms/signals';

@Component({
  selector: 'app-login',
  imports: [FormField],
  template: `
    <form (submit)="onSubmit($event)">
      <input type="email" [formField]="loginForm.email" />
      @if (loginForm.email().touched() && loginForm.email().invalid()) {
        <p class="error">{{ loginForm.email().errors()[0].message }}</p>
      }
      <input type="password" [formField]="loginForm.password" />
      <button [disabled]="loginForm().invalid()">Login</button>
    </form>
  `,
})
export class Login {
  private readonly auth = inject(AuthService);

  // Model signal is the single source of truth.
  protected readonly loginModel = signal({ email: '', password: '' });

  protected readonly loginForm = form(this.loginModel, (path) => {
    required(path.email, { message: 'Email is required' });
    email(path.email, { message: 'Enter a valid email address' });
    required(path.password, { message: 'Password is required' });
  });

  onSubmit(event: Event) {
    event.preventDefault();
    // submit() marks all fields touched, then runs the async action only if valid.
    submit(this.loginForm, async () => {
      await this.auth.login(this.loginModel());
    });
  }
}
```

## Critical rules

| Rule                                                                                            | Correct                                                 |
| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Call the field** to reach its state signals.                                                  | `form.field().valid()`, not `form.field.valid()`        |
| **The model is the source of truth** – never `.set()` the field tree; update the model signal.  | `this.model.update(...)`, not `form.field.set(...)`     |
| **Forbidden `[formField]` attributes** – these are handled by the directive or by schema rules. | Never set `value`/`min`/`max`/`[disabled]`/`[readonly]` |
| **`submit()` callback must be `async`** and return a Promise.                                   | `submit(form, async () => { ... })`                     |
| **Schema paths are not signals** – don't call them; read other fields via context helpers.      | `valueOf(path.x)` / `stateOf(path.x)`, not `path.x()`   |
| **Every component runs on the default `OnPush` + zoneless (v22+).**                             | Do not set `changeDetection`; never opt into `Eager`    |

## Which reference to load

| Task                                                                         | Reference                                                                                          |
| ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Set up a form, read/update model values, display errors, style, reset        | [references/build-a-form.md](references/build-a-form.md)                                           |
| Read field-level or form-level state (valid, touched, dirty, pending…)       | [references/field-state.md](references/field-state.md)                                             |
| Add validation (built-in, debounce, conditional, custom, cross-field, async) | [references/validation.md](references/validation.md)                                               |
| Conditional fields, submission, arrays / dynamic fields                      | [references/dynamic-forms.md](references/dynamic-forms.md)                                         |
| Build a custom form control (`FormValueControl`)                             | [references/custom-controls.md](references/custom-controls.md)                                     |
| Maintain or migrate an existing Reactive Forms area                          | [references/reactive-forms.md](references/reactive-forms.md)                                       |
| Full API surface, common pitfalls, build-error recovery                      | [../angular-developer/references/signal-forms.md](../angular-developer/references/signal-forms.md) |

## Verify – zero errors before done

Run from the repo root and confirm no errors:

- `pnpm exec tsc --noEmit` (or `pnpm build`)
- `pnpm lint`

There is no nx here – do not run `nx lint`.

Files in this skill

  • SKILL.md5.7 KB
  • references/build-a-form.md4 KB
  • references/custom-controls.md3.1 KB
  • references/dynamic-forms.md3.9 KB
  • references/field-state.md1.4 KB
  • references/reactive-forms.md10.3 KB
  • references/validation.md3.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.