Appearance
Important (contexte)
les Signal Forms sont encore expérimentaux et liés à la future version Angular 21 (docs "next"), donc l'API peut encore bouger. Utilisez-les en connaissance de cause et suivez les notes de version.
Les formulaires « orientés signaux » arriveront avec Angular 21 (16 octobre 2025)
Les Signal Forms projettent le système de signals d'Angular au cœur des formulaires : chaque champ, chaque état (valide, en erreur, pending…), devient un signal. Résultat : une UI qui réagit dès qu'une valeur ou une erreur change, sans abonnements manuels. Cette API vit (pour l'instant) dans la doc next et est marquée experimental since v21.0.
Revoir le live
Pourquoi c'est intéressant
- Moins de boilerplate : on décrit quoi valider plutôt que comment écouter.
- Réactivité native : les champs exposent des signals (valeur, erreurs, validité,
submitting
,pending
, etc.). - Typage fort : la forme du formulaire épouse votre modèle TS.
- Migration progressive : peut cohabiter avec les formulaires réactifs classiques.
Qu'est-ce que les formulaires orientés signaux ?
Imaginez que vous remplissez un formulaire d'inscription sur un site web. Traditionnellement, Angular vérifiait les erreurs de validation de manière périodique, comme un contrôleur qui passe dans une salle de classe pour vérifier les copies. Avec les signaux, c'est comme avoir un assistant personnel qui vous prévient immédiatement dès qu'une erreur apparaît ou disparaît, en temps réel !
Les formulaires orientés signaux transforment les formulaires Angular en entités entièrement réactives, où chaque changement de valeur, chaque erreur de validation, et chaque état du formulaire sont des signaux que vous pouvez écouter et réagir instantanément.
Pourquoi cette révolution ?
Les problèmes des formulaires traditionnels
Avant Angular 21, gérer les formulaires nécessitait souvent beaucoup de code boilerplate et des vérifications manuelles de l'état des formulaires. Vous deviez :
- Créer des FormControl, FormGroup manuellement
- Gérer manuellement les validations
- Écouter les changements avec des observables complexes
- Synchroniser l'état entre le formulaire et vos composants
Les avantages des signaux
Les formulaires orientés signaux apportent :
- Réactivité automatique : Plus besoin de souscrire manuellement aux changements
- Syntaxe simplifiée : Moins de code boilerplate
- Performance optimisée : Seules les parties qui changent sont recalculées
- Intégration native : Parfaitement intégrés avec l'écosystème des signaux
Étape 1 — Première approche (le « Hello form »)
Objectif : lier deux inputs à un modèle, avec une validation minimale.
Idées clés
- On déclare un signal de données (le modèle).
- On crée le formulaire avec
form(model, (p) => { … })
. - On relie les
<input>
avec la directive[field]
(à exposer en important la directiveField
). - On enregistre les validateurs déclaratifs (
required
,minLength
,email
, …). Ces helpers sont marqués experimental since v21.0.
Ce que vous verrez dans le code
import { Field, form, required, email } from '@angular/forms/signals'
- Dans le template :
<input [field]="userForm.email">
Pourquoi
[field]
? Parce que l'API expérimentale définit un sélecteur de directive qui relie un champ de formulaire à un contrôle UI ; la structure d'état exposée s'appuie surFieldState
(valeur, erreurs,valid
,pending
,submitting
, etc.).
typescript
import { Component, computed, effect, signal } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Field, FieldPath, form, min, minLength, required, submit } from '@angular/forms/signals';
function groupValidator(path: FieldPath<{
firstName: string
}>) {
required(path.firstName)
minLength(path.firstName, 3)
}
@Component({
selector: 'app-root',
templateUrl: './app.html',
styles: `
.green {
color: green;
}
`,
imports: [Field]
})
export class App {
profile = signal({
firstName: '',
email: ''
})
profileForm = form(this.profile, (path) => {
groupValidator(path)
})
isFirstNameValid = computed(() => this.profileForm.firstName().valid())
nbErrors = computed(() => this.profileForm.firstName().errors().length)
constructor() {
effect(() => {
console.log(this.profileForm().value())
})
}
editProfile(event: Event) {
event.preventDefault()
submit(this.profileForm, async (form) => {
console.log(form().value())
})
}
}
html
<form (submit)="editProfile($event)" novalidate>
<label>Prénom</label>
<input type="text" [field]="profileForm.firstName">
<p [class.green]="isFirstNameValid()">Test</p>
@if (nbErrors() != 0) {
<p>Il y a des erreurs</p>
}
<button>Modifier</button>
</form>
Étape 2 — Aller un peu plus loin (messages d'erreur, état global)
Objectif : afficher des erreurs lisibles et désactiver le bouton Submit tant que le formulaire est invalide.
Points d'attention
field().errors()
renvoie un tableau d'objets d'erreur (pas un objet "dot-access"). Pour tester une erreur précise, parcourez le tableau et vérifiezerror.kind
('required'
,'minLength'
,'email'
, …).- L'état global du formulaire se lit via le champ racine :
userForm().valid
,userForm().value()
,userForm().submitting
, etc. (tous exposés comme signals).
Ce que vous verrez dans le code
- Un
@for
suruserForm.firstName().errors() ?? []
pour afficher des messages. - Un bouton
<button [disabled]="!userForm().valid">…</button>
.
Étape 3 — Approche « avancée » (soumission asynchrone & erreurs serveur)
Objectif : effectuer une soumission asynchrone (HTTP) en utilisant l'utilitaire submit(…)
et refléter l'état (submitting
) côté UI.
Idées clés
submit(form, async action)
valide, gèle les champs le temps de l'action, exposesubmitting
, puis répercute le résultat.- Vous pouvez mapper des erreurs serveur sur des champs (ex. "email déjà utilisé") et les afficher exactement comme des erreurs locales.
- Côté template, utilisez
userForm().submitting
pour un spinner ou pour désactiver les actions. Les propriétés deFieldState
documentent ces états.
Comparaison rapide (mental model)
- Formulaires réactifs classiques : pilotés par
FormGroup
/FormControl
+ Observables (valueChanges
). - Signal Forms : on déclare la logique de validation à la création, puis on réagit via des signals partout dans l'UI (pas d'abonnements manuels). Des retours de la communauté soulignent ce changement de modèle et l'intérêt pour la lisibilité / dynamiques conditionnelles. (Reddit)
Bonnes pratiques & pièges courants
- Tapez votre modèle (évitez
any
) : le field tree (userForm.firstName
) tire son type de votre interface. - Nommez la directive correctement : c'est
[field]
(pas[formControlName]
, ni[control]
). (De nombreux billets parlent de proto/variantes ; fiez-vous aux docs next.) - Erreurs : souvenez-vous que
errors()
⇒ array d'objets{ kind, message? }
. Ne faites paserrors?.required
.
FAQ express
Q. Est-ce que c'est prêt pour la prod ? R. À l'heure où j'écris, c'est expérimental et documenté sur le site "next" : prudence, tests, et surveillez les releases.
Q. Est-ce que ça remplace les Reactive Forms ? R. Pas tout de suite. Pensez "nouvelle option" centrée signals. Les deux approches coexisteront un moment.
Q. Où trouver la doc officielle ? R. Les pages next décrivent les types et propriétés de l'API (FieldState, validateurs required
, etc.).