angular/reference-signal-forms
Explains the mental model and architecture of the code under `packages/forms/signals`. You MUST use this skill any time you plan to work with code in `packages/forms/signals`
npx skills add https://github.com/angular/angular --skill reference-signal-forms
The packages/forms/signals directory contains an experimental, signal-based forms API for Angular.
This system differs significantly from the existing Reactive and Template-driven forms.
WritableSignal<T> which serves as the single source of truth.Unlike Reactive Forms where the FormControl holds the value, here the Signal holds the value.
The form is merely a _view_ or _projection_ of that signal, adding form-specific state (validity, dirty, touched).
form(signal)) returns a FieldTree. This object is a Proxy.It allows accessing nested fields (e.g., myForm.user.name) without manually creating control groups.
Accessing a property on the proxy lazily resolves or creates the corresponding FieldNode.
Schemas are applied to the form structure using functions like apply, applyEach (for arrays), and applyWhen.
This separates the _structure_ of the data from the _rules_ governing it.
[formField] directive binds a DOM element (native input or custom control) to a FieldNode.It handles:
FieldNode (src/field/node.ts)The central internal class representing a single field in the form graph. It aggregates several state managers:
structure: Manages parent/child relationships and signal slicing.validationState: Computes valid, invalid, errors signals.nodeState: Tracks touched, dirty, pristine.metadataState: Stores metadata like min, max, required.submitState: Tracks submission status and server errors.ValidationState (src/field/validation.ts)Manages the complexity of validation:
submit().FormField Directive (src/directive/form_field_directive.ts)The bridge between the FieldNode and the DOM.
[formField]<input>, <select>, <textarea>.FormUiControl or FormValueControl.ControlValueAccessor (via InteropNgControl).Schema (src/api/structure.ts & src/api/rules)Defines the behavior.
schema(fn).apply(path, schema).required, pattern, min, max) and state modifiers (disabled, hidden).form.field.value() reads directly from the underlying signal (projected to the specific path).// 1. Define Model
const user = signal({name: '', age: 0});
// 2. Define Schema
const userRules = schema((u) => {
required(u.name);
min(u.age, 18);
});
// 3. Create Form
const userForm = form(user, userRules); // OR apply(userForm, userRules)
// 4. Bind in Template
// <input [formField]="userForm.name">
packages/forms/signals/src/api/structure.ts: Public API entry points (form, apply).packages/forms/signals/src/api/control.ts: Interfaces for custom controls (FormUiControl).packages/forms/signals/src/field/node.ts: The FieldNode implementation.packages/forms/signals/src/directive/form_field_directive.ts: The [formField] directive.[formField] hooks into type-checking and the runtime.Take angular/reference-signal-forms from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.