Gentleman-Skills angular-forms
install
source · Clone the upstream repo
git clone https://github.com/Gentleman-Programming/Gentleman-Skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Gentleman-Programming/Gentleman-Skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/curated/angular/forms" ~/.claude/skills/gentleman-programming-gentleman-skills-angular-forms && rm -rf "$T"
manifest:
curated/angular/forms/SKILL.mdsource content
When to Use What
| Use Case | Recommendation |
|---|---|
| New apps with signals | Signal Forms (experimental) |
| Production apps | Reactive Forms |
| Simple forms | Template-driven |
Signal Forms (v21+, experimental)
import { form, FormField, required, email } from '@angular/forms/signals'; @Component({ imports: [FormField], template: ` <form> <input [formField]="emailField" type="email" /> <input [formField]="passwordField" type="password" /> <button (click)="submit()">Login</button> </form> ` }) export class LoginComponent { readonly loginForm = form({ email: ['', [required, email]], password: ['', required] }); readonly emailField = this.loginForm.controls.email; readonly passwordField = this.loginForm.controls.password; submit() { if (this.loginForm.valid()) { const values = this.loginForm.value(); } } }
Signal Forms Benefits
- Automatic two-way binding
- Type-safe field access
- Schema-based validation
- Built on signals
Reactive Forms (production)
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; @Component({ imports: [ReactiveFormsModule], template: ` <form [formGroup]="form" (ngSubmit)="submit()"> <input formControlName="email" type="email" /> <input formControlName="password" type="password" /> <button type="submit" [disabled]="form.invalid">Login</button> </form> ` }) export class LoginComponent { private readonly fb = inject(FormBuilder); form = this.fb.nonNullable.group({ email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(8)]], }); submit() { if (this.form.valid) { const { email, password } = this.form.getRawValue(); } } }
Key Points
- ALWAYS use
for type safetyfb.nonNullable.group() - Use
to get typed valuesgetRawValue() - Reactive Forms are synchronous (easier to test)
Nested Forms & FormArray
form = this.fb.nonNullable.group({ name: [''], address: this.fb.group({ street: [''], city: [''], }), phones: this.fb.array([this.fb.control('')]), }); get phones() { return this.form.get('phones') as FormArray; } addPhone() { this.phones.push(this.fb.control('')); }