📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

~4 min de lecture

Angular v20 — NG0201: No provider found for MonServiceXXX

TL;DR

L’erreur NG0201: No provider found for _GetShopProductsUseCase signifie qu’Angular n’a pas trouvé de provider pour la dépendance que ton SignalStore essaye d’injecter.
Corrige-la en enregistrant ce use case dans un injecteur accessible au moment où le store est créé :

  • via @Injectable({ providedIn: 'root' }),
  • ou via providers au niveau de la route/feature,
  • ou via le provider boostrap global.

Contexte

On a une page Boutique qui affiche des produits. Le SignalStore injecte GetShopProductsUseCase pour charger les produits :


@Injectable({ providedIn: 'root' })
export class ShopStore {
  private readonly _getProducts = inject(GetShopProductsUseCase); // <-- l’injection qui casse si pas de provider

  readonly products = toSignal(this._getProducts.execute());
}

Au runtime, Angular remonte la chaîne des injecteurs (composant → route → application) pour trouver un provider de GetShopProductsUseCase.
S’il n’en trouve aucun, il lève NG0201 et te donne un chemin utile :
Path: ShopStore -> _getProducts.


Pourquoi tu obtiens NG0201 ?

Cas fréquents :

  1. Pas d’@Injectable ou pas de providedIn

    export class GetShopProductsUseCase { ... } // <- oublié @Injectable
    
  2. Classe marquée @Injectable, mais non fournie
    Tu as @Injectable() sans providedIn, et aucun providers: [...] ne la déclare.

  3. Provider défini au mauvais endroit
    Tu fournis le use case dans un injecteur non ancêtre du store (ex. provider dans un composant enfant, alors que le store vit plus haut).

  4. Tu injectes une interface TypeScript
    Les interfaces n’existent pas au runtime ⇒ il faut un InjectionToken (ou une classe concrète).

  5. Feature lazy-loaded
    Le store est créé avant le chargement de la feature où tu as défini le provider (donc injecteur indisponible au moment de l’injection).


Visualiser rapidement l’injecteur qui manque

  • Path: ShopStore -> _getProducts te dit : _“Depuis ShopStore, je cherche un provider pour ce type et je n’en trouve pas dans les injecteurs ancêtres.”
  • Si ton store est providedIn: 'root', il est créé avec l’injecteur racine. Il a besoin que ton use case soit accessible depuis cet injecteur (ou depuis un route injector ancêtre si le store est instancié dans la feature).

4 façons propres de corriger

1) Le plus simple : providedIn: 'root'

Idéal quand le use case est stateless et réutilisable partout.


@Injectable({ providedIn: 'root' })
export class GetShopProductsUseCase {
  constructor(private readonly _repo: ProductsGateway) {
  }

  execute(): Observable<Product[]> {
    return this._repo.list();
  }
}
  • ✅ Pas de config supplémentaire.
  • ⚠️ Si tu veux isoler l’implémentation à une feature (ou substituer en test/AB), préfère un provider local ou un token.

2) Fournir la classe au niveau de la route (feature)

Utile en lazy-loading ou si tu veux scoper la durée de vie à la feature.

export const shopRoutes: Routes = [
  {
    path: '',
    component: ShopPage,
    // L’injecteur de route devient parent de tous les enfants de cette feature
    providers: [GetShopProductsUseCase],
  },
];
  • ✅ Scope clair, idéal par feature.
  • ⚠️ Le store doit être créé dans cette feature (ou en dessous).
  • Si le store est providedIn: 'root', il ne verra pas ce provider de route et tu auras la meme erreur.

Astuce : si ton store doit être scopé lui aussi, fournis-le au même niveau (route providers).


3) Fournir au bootstrap (environnement global)

Pour partager des providers “globaux” sans providedIn: 'root'.

bootstrapApplication(App, {
  providers: [
    // Ex. global
    GetShopProductsUseCase,
  ],
});
  • ✅ Disponible partout (comme root).
  • ⚠️ Moins “feature-scoped” que les providers de route.

Check-list de diagnostic

  1. La classe a-t-elle @Injectable ?
  2. Est-elle fournie quelque part (providedIn, providers de route, bootstrap) ?
  3. Le store et le provider vivent-ils dans le même scope (root vs route) ?
  4. Si tu injectes une interface, utilises-tu un InjectionToken ?
  5. Lazy route : le store est-il instancié dans la feature (ou plus bas) ?
  6. En test, as-tu fourni ce qu’il faut (mock/implémentation) ?

Pièges fréquents (et solutions)

  • Interface dans le constructeur

    constructor(private uc: GetShopProducts) {} // ❌ ne marche pas au runtime
    

    → Utilise un InjectionToken<GetShopProducts> + provider.

  • Store providedIn: 'root' + provider de route
    Le store est créé avant la route → il ne voit pas le provider de route.
    → Soit tu passes le store en providers de route aussi, soit tu fournis le use case au root.

  • Classe sans @Injectable
    Même si elle n’injecte rien, une classe doit être décorée (ou fournie via useFactory) pour l’injection par * classe*.


EasyAngularKit te donne toujours des tips concrets.

Envie de creuser davantage tes compétences Angular ? 👉 Rejoins-nous : https://pim.ms/HXcK4tw

📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

EasyAngularKit

Formation complète pour maîtriser Angular et développer des applications web modernes.

Navigation

Contact

Légal

© 2026 Easy Angular Kit. Tous droits réservés.