Skip to content

Formation Angular

Saison 2

Passez un cap sur Angular avec une saison pensée pour la pratique, la montée en niveau et les usages concrets en production.

  • Améliorer sa productivité en utilisant l’IA
  • Utiliser Angular SSR
  • Se préparer à la certification Angular
  • Le challenge du mois

Profitez-en maintenant car le prix augmente bientôt.

Voir la Saison 2

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Angular 22 : le décorateur @Service() pourrait simplifier les services

Angular pourrait bientôt accueillir un nouveau décorateur : @Service().

La nouveauté vient de la PR officielle #68195, ouverte le 14 avril 2026 par Kristiyan Kostadinov, membre de l'équipe Angular. Elle propose d'ajouter @Service() dans @angular/core comme alternative plus directe à @Injectable() pour le cas le plus fréquent : déclarer un service singleton disponible dans toute l'application.

Il faut cependant être précis : au 21 avril 2026, la PR est encore ouverte. Elle est ciblée comme une fonctionnalité mineure, porte une annotation @developerPreview 22.0 dans le code proposé, et Angular annonce Angular 22.0 pour la semaine du 1er juin 2026. Autrement dit, c'est une piste très concrète, mais pas encore une API stable à utiliser en production.

Le problème que @Service() veut résoudre

Aujourd'hui, le service Angular classique ressemble à ceci :

ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class PostStore {
  private readonly http = inject(HttpClient);

  getPosts() {
    return this.http.get('/api/posts');
  }
}

Ce code fonctionne très bien. providedIn: 'root' indique à Angular que le service est fourni par l'injecteur racine : il est disponible partout, créé à la demande, et reste tree-shakable si personne ne l'utilise.

Mais il y a un décalage entre le nom du décorateur et l'intention. @Injectable() signifie seulement "cette classe peut participer à l'injection de dépendances". Dans la pratique, on l'utilise très souvent pour dire "ceci est un service d'application".

Ce décalage est devenu plus visible depuis l'évolution du style Angular. Les noms courts sont maintenant favorisés : un fichier book-store.ts et une classe BookStore remplacent souvent book-store.service.ts et BookStoreService. Le rôle de la classe est donc moins visible dans le nom. Un décorateur @Service() rendrait cette intention explicite.

À quoi ressemblerait le nouveau décorateur ?

La PR propose une syntaxe très courte :

ts
import { Service, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Service()
export class PostStore {
  private readonly http = inject(HttpClient);

  getPosts() {
    return this.http.get('/api/posts');
  }
}

Par défaut, @Service() serait fourni dans root. Il remplacerait donc le cas courant :

ts
@Injectable({ providedIn: 'root' })

par :

ts
@Service()

Ce n'est pas seulement un alias cosmétique. La PR dessine une API plus stricte, plus orientée vers les pratiques modernes d'Angular.

Les trois différences importantes avec @Injectable()

1. root par défaut

Avec @Injectable(), il faut expliciter le provider :

ts
@Injectable({ providedIn: 'root' })
export class UserStore {}

Avec @Service(), le comportement par défaut serait déjà celui-là :

ts
@Service()
export class UserStore {}

Pour la majorité des services d'application, cela retire du bruit sans changer le modèle mental : un service singleton, partagé, créé par Angular quand il est injecté.

2. inject() au lieu de l'injection par constructeur

La PR interdit l'injection de dépendances par constructeur dans une classe @Service().

Ce code serait donc à éviter :

ts
@Service()
export class UserStore {
  constructor(private readonly http: HttpClient) {}
}

Le style attendu est plutôt :

ts
@Service()
export class UserStore {
  private readonly http = inject(HttpClient);
}

C'est cohérent avec la direction prise par Angular depuis l'arrivée de inject(). Les dépendances sont déclarées au plus près des champs qui les utilisent, et la classe n'a plus besoin d'un constructeur uniquement pour recevoir des services.

3. Moins de configurations avancées

@Injectable() accepte des configurations historiques et très souples. On peut y retrouver des patterns avancés comme useClass, useValue, useFactory ou des scopes de provider différents.

@Service() serait volontairement plus petit. D'après la PR, il supporte surtout :

ts
@Service({
  factory: () => ({ value: 'demo' })
})
export class DemoStore {}

et :

ts
@Service({ autoProvided: false })
export class LocalStore {}

Avec autoProvided: false, le service ne serait pas fourni automatiquement. Il faudrait alors le déclarer explicitement dans une liste de providers :

ts
@Component({
  providers: [LocalStore],
  template: `...`
})
export class FeatureComponent {}

Ce point est important : @Service() ne cherche pas à couvrir tous les usages de @Injectable(). Il cible le cas standard, celui que les applications utilisent le plus souvent.

Est-ce que @Injectable() disparaît ?

Non. Rien, dans la PR, n'indique que @Injectable() va disparaître.

@Injectable() reste nécessaire pour les cas plus précis :

  • fournir une dépendance avec une configuration avancée ;
  • travailler avec des scopes particuliers comme platform ;
  • conserver un style compatible avec des bases de code existantes ;
  • utiliser des patterns de DI qui ne rentrent pas dans le cadre volontairement réduit de @Service().

Il vaut mieux lire @Service() comme une nouvelle porte d'entrée pour le cas courant, pas comme un remplacement complet de l'API historique.

Pourquoi l'API fait déjà débat

La discussion GitHub montre que la proposition n'est pas anecdotique. Certaines personnes apprécient l'intention : le décorateur rend le rôle de la classe plus clair et supprime le providedIn: 'root' répétitif.

D'autres soulignent plusieurs risques :

  • ajouter un deuxième décorateur peut augmenter la charge cognitive ;
  • la documentation existante autour de @Injectable({ providedIn: 'root' }) resterait massive ;
  • les cas avancés pourraient obliger à mélanger @Service() et @Injectable() dans un même projet ;
  • le typage de factory et le support de scopes comme platform sont encore discutés.

C'est exactement pour cela qu'il faut présenter la nouveauté avec prudence. Une API en Developer Preview peut changer rapidement. Angular documente clairement que les APIs en Developer Preview ne sont pas encore couvertes par les garanties habituelles de stabilité sémantique.

Faut-il préparer une migration ?

Pour l'instant, non.

Si @Service() arrive dans Angular 22, il faudra probablement le tester d'abord dans du code non critique, sur des services simples :

ts
@Service()
export class PreferencesStore {
  private readonly http = inject(HttpClient);
}

En revanche, il n'y a aucune urgence à transformer tous les services existants. Un service actuel comme celui-ci reste lisible, supporté et parfaitement valide :

ts
@Injectable({ providedIn: 'root' })
export class PreferencesStore {
  private readonly http = inject(HttpClient);
}

Le bon réflexe sera donc progressif :

  • garder @Injectable() dans les applications existantes ;
  • observer l'évolution de la PR et de la documentation Angular 22 ;
  • réserver @Service() aux services simples si l'API est bien livrée ;
  • conserver @Injectable() pour les besoins avancés.

Ce qu'il faut retenir

@Service() est une petite API, mais elle raconte une tendance plus large dans Angular : rendre les intentions plus explicites et pousser les patterns modernes par défaut.

Si la PR est acceptée, déclarer un service root pourrait devenir aussi direct que :

ts
@Service()
export class UserStore {}

Mais tant que la fonctionnalité n'est pas mergée et documentée dans une version Angular publiée, elle doit rester une information de veille, pas une consigne de migration.