~7 min de lecture
Angular 22 : le guide complet de la release signal-first
Ça y est, Angular 22 est sorti. Si Angular 21 posait les fondations (zoneless par défaut, Vitest, Tailwind natif), la v22 assume pleinement la direction prise depuis la v17 : moins de magie, signals partout, et un grand ménage dans les APIs historiques.
C'est une release de consolidation. Plusieurs features longtemps expérimentales passent en stable, des choix par défaut changent (parfois de façon breaking), et tout un pan d'API ViewEngine et NgModule disparaît pour de bon.
Dans cet article, on passe en revue ce qui change concrètement :
OnPushdevient le change detection par défaut,- les Signal Forms rejoignent l'API publique,
FetchBackenddevient le backend HTTP par défaut,- le nouveau décorateur
@Serviceet la fonctioninjectAsync(), - le support des outils MCP côté web,
- un compilateur plus strict,
- et la suppression d'un paquet d'APIs dépréciées.
Petit prérequis avant de commencer : la v22 demande TypeScript 6.0 minimum. Les versions antérieures de TS ne sont plus supportées, à garder en tête avant de lancer la migration.
1. OnPush devient la stratégie par défaut
C'est le changement de comportement le plus impactant de la v22.
Jusqu'ici, un composant sans changeDetection explicite utilisait ChangeDetectionStrategy.Default. En Angular 22, un
composant dont la stratégie est undefined bascule automatiquement en OnPush.
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.html',
// Aucune stratégie précisée : OnPush s'applique automatiquement en v22
})
export class Card {}
Concrètement, le framework pose la change detection précise et basée sur les signals comme norme par défaut. Dans la
majorité des cas modernes (signals, input(), composants déjà en OnPush), tu ne verras aucune différence, c'est même
ce que tu voulais.
Si tu as encore des composants qui s'appuient sur le comportement « tout vérifier » de Default (mutation d'objets,
setTimeout qui modifie l'état sans passer par un signal), tu peux explicitement revenir en arrière avec le nouvel
alias Eager :
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-legacy-widget',
templateUrl: './legacy-widget.html',
changeDetection: ChangeDetectionStrategy.Eager, // ancien comportement "Default"
})
export class LegacyWidget {}
Eager est l'alias cosmétique de l'ancien Default. Le message est clair : OnPush n'est plus une optimisation, c'est
la base.
2. Les Signal Forms passent en API publique
Le feuilleton des Signal Forms touche à sa fin. Après plusieurs minors où l'API se stabilisait (cf. la 21.2), les Signal Forms graduent officiellement dans l'API publique en v22.
On reste sur l'API form() introduite en 21.x, qui infère les types depuis le modèle :
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { form, submit } from '@angular/forms/signals';
@Component({
selector: 'app-profile',
templateUrl: './profile.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Profile {
protected readonly profileForm = form({
email: '',
age: 0,
});
protected onSubmit(): void {
submit(this.profileForm, {
action: (value) => this.save(value),
});
}
private save(value: { email: string; age: number }): void {
// ...
}
}
La v22 ajoute trois capacités utiles :
reloadValidation()pour redéclencher manuellement une validation asynchrone (par exemple après un retry réseau) ;- une option de debounce dans
validateAsyncetvalidateHttp; - la méthode
FieldState.getError()pour récupérer une erreur précise d'un champ.
// Lire une erreur ciblée sur un champ
const emailError = this.profileForm.email().getError('async');
// Forcer une revalidation
this.profileForm.email().reloadValidation();
Côté validation, attention à un breaking change : les règles min et max n'acceptent plus de valeurs string. Il
faut désormais passer un number ou null.
Si tu attendais la stabilisation pour migrer tes formulaires en production, c'est le moment de t'y mettre sérieusement.
3. FetchBackend devient le backend HTTP par défaut
Grosse simplification côté HTTP : FetchBackend est maintenant l'implémentation par défaut du HttpClient. Plus besoin
d'opt-in explicite via withFetch().
Conséquence directe : withFetch() est déprécié et peut être retiré sans risque.
import { provideHttpClient } from '@angular/common/http';
// Avant (v21)
provideHttpClient(withFetch());
// v22 : fetch est déjà le défaut
provideHttpClient();
Deux autres évolutions à connaître :
reportProgressest déprécié, remplacé par deux options plus explicites :reportUploadProgressetreportDownloadProgress.Le suivi de progression d'upload repose sur XHR. Si tu en as besoin, il faut explicitement repasser sur le backend XHR, qui utilise
HttpXhrBackend:import { provideHttpClient, withXhr } from '@angular/common/http'; provideHttpClient(withXhr());
Enfin, côté SSR, une taille de buffer maximale a été introduite pour les requêtes fetch, afin d'éviter de gonfler
la mémoire serveur sur de grosses réponses.
4. Le décorateur @Service et injectAsync()
La v22 introduit un nouveau décorateur @Service. Dans la lignée du découplage NgModule, il offre une sémantique
plus claire que @Injectable() pour déclarer un service applicatif.
Côté injection, la nouvelle fonction injectAsync() permet de résoudre une dépendance de manière asynchrone, utile
pour le lazy loading de providers ou les ressources chargées dynamiquement.
import { injectAsync, Service } from '@angular/core';
@Service()
export class CatalogService {
private readonly engine = injectAsync(() =>
import('./catalog-engine').then((m) => m.CatalogEngine),
);
}
À cela s'ajoutent plusieurs raffinements côté core :
- caching des
resource()en SSR : les ressources résolues côté serveur ne sont plus refetchées à l'hydratation ; - bootstrap dans un shadow root désormais supporté ;
- dé-duplication des host directives ;
- support des
IdleRequestOptionspour l'Idle service.
5. Support des outils MCP côté web
Angular 22 ajoute le support des Web MCP Tools. Le Model Context Protocol devient un citoyen de première classe pour connecter les outils d'IA au contexte de ton application et de ta codebase.
C'est dans l'air du temps : après le tooling pensé pour l'IA (schematics, llms.txt), Angular câble une brique standard
pour les agents qui consomment et manipulent le code. Si tu construis des intégrations IA autour de ton app, c'est une
porte d'entrée officielle plutôt qu'un bricolage maison.
6. Un compilateur (beaucoup) plus strict
La v22 muscle l'analyse statique des templates. Plusieurs diagnostics se déclenchent désormais sur les projets existants, attends-toi à des erreurs de compilation à la mise à jour :
nullishCoalescingNotNullableetoptionalChainNotNullable: Angular te prévient quand tu utilises??ou?.sur une valeur qui ne peut jamais êtrenullouundefined(code mort) ;- les éléments qui matchent plusieurs sélecteurs lèvent désormais une erreur à la compilation ;
- les attributs préfixés par
data-ne bindent plus d'inputs ni d'outputs ; - lier un même target en input et output lève une erreur ;
- les variables
inlèvent une erreur dans les expressions de template.
Bonne nouvelle en contrepartie : le type narrowing s'améliore. La navigation sûre (?.) affine désormais
correctement le type des valeurs nullables, et l'optional chaining renvoie bien undefined.
@if (user()?.profile; as profile) {
{{ profile.displayName }}
}
Ces diagnostics peuvent piquer au moment du ng update, mais ils révèlent du code mort ou des bindings ambigus. C'est un
bon ménage.
7. Le Router évolue
Plusieurs ajustements côté routing, dont un changement de défaut à surveiller :
paramsInheritanceStrategypasse à'always'par défaut (au lieu de'emptyOnly'). Les enfants héritent désormais systématiquement des params et data des parents ;- dans un
CanMatchFn, le paramètrecurrentSnapshotdevient obligatoire, etcanMatchreçoit unActivatedRouteSnapshotpartiel ; - nouveau support de
browserUrlsur les router links ; - nouvelle option
unmatchedInputBehavioret un paramètreoptionspourwithComponentInputBinding; TitleStrategy.getResolvedTitleForRoute()renvoie maintenantstring | undefined(fini leany).
Et un retrait : provideRoutes() disparaît, utilise provideRouter() ou le token ROUTES.
import { provideRouter } from '@angular/router';
// Avant : provideRoutes(routes)
// v22 :
provideRouter(routes);
8. Le grand ménage : APIs supprimées
La v22 assume la suppression d'un paquet d'APIs dépréciées de longue date. Si ton app traîne du legacy, c'est ici que ça va casser :
| API supprimée | Remplacement |
|---|---|
ComponentFactoryResolver / ComponentFactory |
Passer la classe du composant directement (createComponent()) |
createNgModuleRef() |
createNgModule() |
ChangeDetectorRef.checkNoChanges() |
fixture.detectChanges() dans les tests |
provideRoutes() |
provideRouter() ou ROUTES |
| Intégration Hammer.js | Implémentation custom des gestes |
getAngularLib() / setAngularLib() |
getAngularJSGlobal() / setAngularJSGlobal() |
À noter aussi : appRef.bootstrap() n'accepte plus any comme second argument (et l'élément ne peut plus être
nullable), et les animations de leave ne sont plus limitées aux éléments réellement retirés du DOM.
9. En résumé : ce qu'il faut retenir
| Changement | Impact | Action |
|---|---|---|
OnPush par défaut |
Élevé | Vérifier les composants en Default, sinon Eager |
| Signal Forms stables | Élevé | Migrer les formulaires, min/max en number |
FetchBackend par défaut |
Élevé | Retirer withFetch(), migrer reportProgress |
| TypeScript 6 minimum | Élevé | npm i -D typescript@6 avant la migration |
| Compilateur plus strict | Moyen | Corriger les diagnostics au build |
Router (paramsInheritance, etc.) |
Moyen | Vérifier l'héritage des params et CanMatchFn |
@Service + injectAsync() |
Faible | Adopter progressivement |
| Web MCP Tools | Faible | Pour les intégrations IA |
| Suppression APIs legacy | Variable | Suivre la table de remplacement |
Pour mettre à jour :
ng update @angular/core@22 @angular/cli@22
Les schematics de migration gèrent une bonne partie du travail automatiquement, notamment les renommages d'API. Mais vu
le nombre de changements de défaut (OnPush, FetchBackend, paramsInheritanceStrategy), prévois une passe de tests
sérieuse derrière.
Angular 22, c'est la confirmation d'un cap : signal-first, strict par défaut, et débarrassé de son héritage ViewEngine.
Si tu es déjà en zoneless avec signals et OnPush, la migration sera quasi indolore. Sinon, c'est l'occasion de solder
la dette.