About angular-forms
A skill for Angular developers that explains when to use Signal Forms, Reactive Forms, or template-driven forms. It also covers typed fields, validation, and nested form structures.
Key features
- 01Compare three form approaches
- 02Access typed Signal Forms fields
- 03Build typed synchronous Reactive Forms
- 04Create nested forms and FormArray
Use cases
- 01Choose Signal Forms for new apps
- 02Build Reactive Forms for production apps
- 03Use template-driven forms for simple forms
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ gentleman-programming/ gentleman-skills --skill angular-formsAfter installing, type/angular-formsin Claude Code to confirm it loaded.
Details
From the project READMEWhen 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
fb.nonNullable.group()for type safety - Use
getRawValue()to get typed values - 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(''));
}
Resources
Comments
Markdown supported