Angular 21 : Les Nouvelles Fonctionnalités
Ça y est Angular 21 et la fonctionnalité tant attendue est sortie.
Découvre ici toutes les features d'Angular 21, une belle avancée pour Angular.
Signal Form
C'est certes expérimental, ça amène quand même un grand changement dans la gestion des formulaires pour Angular.
Tu le sais sans doute, Angular a intégré voilà quelques versions les signaux. Et les forms n'avaient pas encore reçu leur refonte en signal.
Pour déclarer :
On travaille donc avec un signal, et une form.
protected readonly player = signal(createEmptyPlayer())\nprotected playerForm = form(this.player)Nous avons alors une form, simple, sans validator. Et c'est là que ça devient intéressant !
On peut construire des schémas de validation. Ici, je te montre un exemple simple. Tu as plusieurs operators de validations (min, max, …)
export const playerSchema = schema<Player>(user => {\n required(user.name)\n})Et l'ajouter Ă notre form :
protected readonly player = signal(createEmptyPlayer())\nprotected playerForm = form(this.player, playerSchema)Tu peux même utiliser Zod pour créer un schéma de validation !
export const cardSchema = z.object({\n id: z.string().min(1, "Id is required"),\n name: z.string().min(3, "Name is required")\n});\n\nexport type Card = z.infer<typeof cardSchema>;\n\n\nprivate readonly card = signal<Card>({ id: '', name: '' });\nprotected readonly cardForm = form(this.card, card => {\n validateStandardSchema(card, cardSchema)\n})Pour connecter ton formulaire côté template, tu as juste à appeler : [field] (Directive Field à importer dans ton component ts/js)
<form>\n<label>\n Label\n </label>\n <input type="text" [control]="cardForm.name" />\n\n @for (error of cardForm().errors(); track error.kind) {\n <span>{{ error.message }}</span>\n }\n\n<button type="submit" [disabled]="cardForm().invalid()" >Créer</button>\n</form>Comme tu peux le noter aussi, pour vérifier si la form est valide, tu vas vérifier via un signal si c'est valid ou invalid !
<button type="submit" [disabled]="cardForm().invalid()" >Créer</button>Et tu as la liste des erreurs qui est un signal aussi ❤
@for (error of cardForm().errors(); track error.kind) {\n <span>{{ error.message }}</span>\n}Et pour récupérer les saisies lors de la sauvegarde, value est là pour toi, simplement :
addCard() {\n if(this.sessionForm().valid()) {\n const result = this.sessionForm().value()\n }\n }Imagine un set, ou un update d'une valeur de ton model, tu le sets avec :
this.sessionForm.title().value.set('Forest');Aller plus loin avec les validators
Petite parenthèse, tu peux vraiment construire des formulaires plus élaborés :
protected readonly cardForm = form(this.card, card => {\n required(card.name),\n required(card.description, { message: 'Description is required', when: ({ valueOf }) => valueOf(card.name) !== '' && valueOf(card.name) === 'magic' }),\n\n validate(card, context => {\n if(context.value().mana === 'forest') {\n return customError({ message: 'forest is not allowed' });\n }\n\n return null;\n })\n });Tu noteras ici la possibilité de valider de manière global pour tous les champs, ou pour un validator, en le customisant. Et même ajouter des validators asynchrones !
protected readonly sessionForm = form(this.session, session => {\n required(session.name);\n required(session.description, {message: 'plouf', when: ({ valueOf }) => valueOf(session.name) !== '' && valueOf(session.name).length > 3});\n min(session.winnerId, 1, { message: 'Winner is required'});\n\n validate(session, s => {\n if(s.value().durationMinutes > 30) {\n console.info('Duration is too long');\n return customError({ kind: 'durationMinutes', message: 'Too long session' });\n }\n\n return null;\n }),\n validateAsync(session.notes, {\n params: ({ valueOf }) => ({ name: valueOf(session.notes) }),\n factory: () => this.tryValidate,\n errors: (value) => {\n console.info('error ?', err)\n return null;\n }\n })\n });Le tryValidate est une resource !
private readonly tryValidate = resource({\n defaultValue: false,\n loader: async (params) => {\n await new Promise(resolve => setTimeout(resolve, 1000));\n console.info('validating ?', params)\n return true\n }\n });Alors, tu en penses quoi ? Sacré changement, n'est-ce pas ?
Sur ma chaîne : Angular modern stack, pour les abonnés, je propose même comment convertir une reactiveForm en signal Form :)
Zoneless par défaut
Angular 21 marque une étape majeure pour le framework : la fin officielle de zone.js !
Plus besoin de mettre le provide dans ta config initiale !
C'est inclus par défaut !
HttpClient par défaut
Il en est de mĂŞme pour httpClient ! Ne cherche plus dans ton app.config, plus besoin de l'importer !
Il est présent par défaut également.
Amélioration du MCP
Je t'en avais parlé voilà quelque temps sur la chaîne Angular modern stack, le server MCP a été mis à jour et prend en compte maintenant : ai-tutor !
Tu ne connais pas ?
C'est un agent qui peut t'accompagner pour apprendre Angular de manière progressive !
Très prochainement, je le teste pour voir ce qu'il en ressort :D
Angular Aria
Une surprise (une belle) pour cette version 21, c'est l'arrivée d'Angular Aria.
Un nouveau package dédié pour l'accessibilité !
npm install @angular/ariaAria package te propose une liste de directives à inclure pour améliorer l'accessibilité.
Exemple :
<div ngToolbarWidgetGroup role="radiogroup" class="group" aria-label="Text alignment options">\n <button\n ngToolbarWidget\n role="radio"\n type="button"\n value="align left"\n aria-label="align left"\n #leftAlign="ngToolbarWidget"\n class="material-symbols-outlined"\n [aria-checked]="leftAlign.selected()"\n translate="no">\n format_align_left\n </button>Conclusion
Angular 21 est une belle version qui marque un changement clivant pour ce framework !
Signal form c'est le game changer d'Angular !
On a hâte de voir l'avancée sur les resources, pour bien valider toute cette partie bien intéressante pour Angular 22 peut-être ?