~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
providersau 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 :
Pas d’
@Injectableou pas deprovidedInexport class GetShopProductsUseCase { ... } // <- oublié @InjectableClasse marquée
@Injectable, mais non fournie
Tu as@Injectable()sansprovidedIn, et aucunproviders: [...]ne la déclare.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).Tu injectes une interface TypeScript
Les interfaces n’existent pas au runtime ⇒ il faut unInjectionToken(ou une classe concrète).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 -> _getProductste dit : _“DepuisShopStore, 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
- La classe a-t-elle
@Injectable? - Est-elle fournie quelque part (
providedIn,providersde route, bootstrap) ? - Le store et le provider vivent-ils dans le même scope (root vs route) ?
- Si tu injectes une interface, utilises-tu un
InjectionToken? - Lazy route : le store est-il instancié dans la feature (ou plus bas) ?
- 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 viauseFactory) 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