Angular 22

Angular 22 Signal Forms — Profile Demo

Signal forms are now stable in v22! Demonstrating reloadValidation(), getError(), validateAsync, validateHttp, debounce, ngNoCva, SignalFormControl, and submit().

validateAsync with resource() • debounce(p.username, 400) • reloadValidation() • getError('minLength')

validateHttp with httpResource • debounce(p.emailAddress, 500) • reloadValidation()

getError('required') • getError('maxLength')?.maxLength for typed access

getError('min')?.min • getError('max')?.max

ngNoCva opts out of ControlValueAccessor • pattern(p.website, /^https?:\/\/.+/)

0/500 characters

validate() custom sync validator • maxLength(p.bio, 500)


SignalFormControl (Reactive Forms Compat)

SignalFormControl bridges signal forms into a traditional FormGroup. Use ngNoCva on the input to prevent the CVA from interfering with [formField].

new SignalFormControl('', p => { minLength(p, 2); }) • ngNoCva on input • [formField]="nicknameControl.fieldTree"


Form View Component (FVC)

Build custom form controls by implementing FormValueControl<T>. The only requirement is a value model signal. Use them with [formField] just like native inputs.

0 / 5
Value: 0Valid: falseTouched: false

FormValueControl<number> • value = model(0) • Auto-syncs disabled, touched, name, required

// 1. Create a component implementing FormValueControl<T>
@Component({ selector: 'app-rating-control', ... })
export class RatingControl implements FormValueControl<number> {
  readonly value = model(0);       // ← required: model signal
  readonly disabled = input(false); // ← optional: auto-synced
  readonly touched = model(false);  // ← optional: auto-synced
  readonly name = input('');        // ← optional: auto-synced
  readonly required = input(false); // ← optional: auto-synced
}

// 2. Use it with [formField] ,  just like a native input!
<app-rating-control [formField]="ratingForm.rating" />

// The Field directive automatically:
// - Two-way binds value via the model() signal
// - Syncs disabled, touched, name, required
// - Reports validation errors

Breakingmin / max Validators: Numbers Only

The min and max validators (both in Validators and signal forms) no longer accept string values. You must pass a number. This applies to Reactive Forms' Validators.min() / Validators.max() and signal forms' min() / max().

BeforeValidators.min('5') — accepted strings silently
AfterValidators.min(5) — number required, string is a type error
The Age field above uses min(p.age, 13) and max(p.age, 150) — both require numeric arguments. If you previously passed strings like min(p.age, '13'), you'll get a compile error in Angular 22.
// BEFORE (Angular ≤21) — min/max accepted strings
import { Validators } from '@angular/forms';

// These worked but were error-prone:
Validators.min('5')   // ← accepted string '5'
Validators.max('100') // ← accepted string '100'

// AFTER (Angular 22) — numbers only!
Validators.min(5)     // ✅ number required
Validators.max(100)   // ✅ number required

// Validators.min('5')  // ❌ Type error: string not assignable to number

// Signal Forms (same rule):
min(p.age, 13);       // ✅ number only
max(p.age, 150);      // ✅ number only

Angular 22 API Highlights

  • reloadValidation() — Now a first-class method on FieldState. Reloads all async validators for a field and its descendants.
  • getError(kind) — Typed, reactive error access on FieldState. Returns the typed error instance (e.g. getError('min').min) or undefined.
  • ngNoCva — Attribute to opt out of ControlValueAccessor binding. Useful when using [formField] inside a [formGroup] to prevent CVA conflicts.
  • debounce(path, ms | 'blur') — Debounces UI-to-model updates. Async validators also naturally debounce via the Resource reactivity cycle.
  • SignalFormControl — Bridges signal forms into a FormGroup. Access the underlying FieldTree via .fieldTree.
  • Signal Forms are now stable! — Graduated from experimental to @publicApi 22.0.