Skip to content

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

Les Injecteurs Hiérarchiques d'Angular ​

Imaginez que vous avez une boîte à outils dans votre garage (service global), mais aussi des outils spécifiques dans chaque pièce de votre maison (services locaux). Angular fonctionne de la même manière avec ses injecteurs hiérarchiques !

Pourquoi c'est important ? ​

Quand vous utilisez inject(UserService) dans un composant, Angular doit savoir où trouver ce service. Il y a plusieurs endroits possibles, et Angular suit un ordre précis pour le chercher.

Analogie simple

C'est comme chercher un livre :

  1. D'abord dans votre chambre (composant)
  2. Puis dans le salon (composant parent)
  3. Enfin dans la bibliothèque commune (service global)

Les deux types d'injecteurs ​

Angular utilise deux types d'injecteurs qui travaillent ensemble :

  • Element Injectors (liés aux composants) Ces injecteurs sont créés avec vos composants et disparaissent quand le composant est supprimé.

  • Environment Injectors (liés à l'application) Ces injecteurs gèrent les services globaux et ceux des routes.

🎯 Element Injectors - Pour l'isolation et le cycle de vie ​

Problème résolu : Comment isoler des services temporaires sans polluer l'application ?

typescript
// Sans Element Injectors, ceci polluerait l'application globale
@Component({
  selector: 'app-modal',
  template: '<div>Modal content</div>'
})
export class ModalComponent {
  // Ce service ne devrait exister QUE pendant que la modal est ouverte
  private modalState = inject(ModalStateService);
}

Avantages :

  • ✅ Nettoyage automatique : le service disparaît avec le composant
  • ✅ Isolation : pas de conflit avec d'autres composants
  • ✅ Performance : pas de services inutiles en mémoire

🌍 Environment Injectors - Pour le partage et la persistance ​

Problème résolu : Comment partager des services entre plusieurs composants ?

typescript
// Ces services doivent être partagés et persister
@Injectable({ providedIn: 'root' })
export class AuthService {
  // Doit être accessible partout et rester en vie
}

@Injectable({ providedIn: 'root' })
export class ConfigService {
  // Configuration globale de l'application
}

Avantages :

  • ✅ Partage : même instance entre tous les composants
  • ✅ Persistance : reste en vie pendant toute l'application
  • ✅ Performance : une seule instance créée

🧠 L'analogie de la maison ​

Imaginez votre maison :

  • Element Injectors = Chaque pièce (chambre, cuisine, salon)

    • Chaque pièce a ses propres outils spécifiques
    • Quand vous quittez la pièce, les outils restent dedans
    • Pas de pollution entre les pièces
  • Environment Injectors = Garage commun + Cave

    • Outils partagés par toute la famille
    • Restent disponibles en permanence
    • Accessibles depuis n'importe quelle pièce

Comment Angular trouve un service ​

Quand vous écrivez inject(UserService), Angular suit cet ordre :

  1. Dans le composant : cherche d'abord dans l'injecteur du composant courant
  2. Remonte dans l'arbre : regarde dans les composants parents
  3. Dans la route : cherche dans l'injecteur de la route courante
  4. Globalement : enfin dans l'injecteur global de l'application
  5. Erreur : si rien n'est trouvé, affiche "No provider for UserService!"

Attention

Si Angular ne trouve pas le service, vous verrez l'erreur "No provider for X!" dans la console.

Element Injectors : services liés aux composants ​

Les Element Injectors sont créés avec vos composants et disparaissent quand le composant est supprimé.

Exemple simple ​

typescript
import { Component, inject } from '@angular/core';
import { UserCardService } from './user-card.service';

@Component({
  selector: 'app-user-card',
  template: '<div>{{ user.name }}</div>',
  providers: [UserCardService] // Service uniquement pour ce composant
})
export class UserCardComponent {
  private userCardService = inject(UserCardService);
  user = input<User>();
}

Quand utiliser un Element Injector ? ​

  • Service temporaire : logique spécifique à un composant
  • Isolation : éviter qu'un service soit partagé entre composants
  • Nettoyage automatique : le service disparaît avec le composant

Environment Injectors : services globaux et par route ​

Les Environment Injectors gèrent les services qui persistent dans l'application.

Service global ​

typescript
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root' // Disponible partout dans l'application
})
export class AuthService {
  // Instance unique partagée
}

Service par route ​

typescript
import { Routes } from '@angular/router';
import { AdminService } from './admin.service';
import { UserListComponent } from './user-list.component';

export const routes: Routes = [
  {
    path: 'admin',
    providers: [AdminService], // Disponible pour cette route
    children: [
      { path: 'users', component: UserListComponent }
    ]
  }
];

Quand utiliser un Environment Injector ? ​

  • Service global : authentification, configuration
  • Service par feature : logique métier d'un module
  • Persistance : le service reste en vie pendant la navigation

Options avancées d'injection ​

Par défaut, inject() suit l'ordre hiérarchique standard. Mais vous pouvez contrôler précisément où Angular cherche votre service avec des options spéciales.

Pourquoi contrôler la recherche ?

Parfois vous voulez :

  • Un service local uniquement (pas celui du parent)
  • Ignorer le service local et prendre celui du parent
  • Un service optionnel (qui peut ne pas exister)
  • Limiter la recherche à un niveau spécifique

🟦 { self: true } - Cherche seulement ici ​

Signification : "Regarde uniquement dans mon composant, ne remonte pas au parent."

typescript
@Component({
  selector: 'app-child',
  template: '<div>Child</div>',
  providers: [LocalService] // Obligatoire ici !
})
export class ChildComponent {
  // Angular ne cherchera QUE dans ce composant
  private localService = inject(LocalService, { self: true });
}

Attention

Si vous oubliez de fournir le service dans ce composant, Angular ne le cherchera pas chez le parent : il plantera !

Cas d'usage : Service temporaire, logique isolée, éviter les conflits avec le parent.

🟨 { skipSelf: true } - Ignore-moi, cherche chez le parent ​

Signification : "Ne prends pas celui d'ici, va directement chez le parent."

typescript
@Component({
  selector: 'app-parent',
  providers: [ParentService],
  template: '<app-child></app-child>'
})
export class ParentComponent {}

@Component({
  selector: 'app-child',
  providers: [ParentService], // Angular va ignorer celui-ci
  template: '<div>Child</div>'
})
export class ChildComponent {
  // Angular ignore le ParentService du Child et prend celui du Parent
  private parentService = inject(ParentService, { skipSelf: true });
}

Cas d'usage : Éviter les conflits, forcer l'utilisation du service parent.

🟩 { optional: true } - Si tu ne le trouves pas, pas grave ​

Signification : "Si le service n'existe pas, ne plante pas — donne-moi juste null."

typescript
@Component({
  selector: 'app-flexible',
  template: '<div>Flexible component</div>'
})
export class FlexibleComponent {
  // Ne plante pas si LoggerService n'existe pas
  private logger = inject(LoggerService, { optional: true });

  constructor() {
    if (this.logger) {
      this.logger.log('Logger trouvé !');
    } else {
      console.log('Pas de logger, et tout va bien');
    }
  }
}

Cas d'usage : Composants réutilisables, directives optionnelles, services de debug.

🏠 { host: true } - Ne dépasse pas la limite de l'hôte ​

Signification : "Cherche le service seulement jusqu'à l'hôte (le composant sur lequel je suis appliqué)."

typescript
@Directive({
  selector: '[tooltip]'
})
export class TooltipDirective {
  // Cherche dans la directive ET le composant qui la porte, mais pas plus haut
  private logger = inject(LoggerService, { host: true });

  constructor() {
    this.logger?.log('Tooltip active');
  }
}

Cas d'usage : Directives, composants réutilisables, éviter la pollution globale.

📊 Résumé des options ​

OptionImage mentale 🧠Que fait Angular ?
{ self: true }🔒 "Cherche seulement ici"Ne regarde que dans le composant courant
{ skipSelf: true }⏭️ "Saute-moi"Ignore l'injecteur courant, regarde chez le parent
{ optional: true }🎩 "Pas grave s'il n'existe pas"Retourne null au lieu d'une erreur
{ host: true }🏠 "Ne sors pas de la maison"Cherche jusqu'à l'hôte, pas plus haut

🧠 Combinaisons possibles ​

Vous pouvez combiner plusieurs options :

typescript
// Cherche chez le parent, et si pas trouvé, ne plante pas
const logger = inject(LoggerService, { skipSelf: true, optional: true });

// Cherche seulement ici, mais ne plante pas si absent
const localLogger = inject(LoggerService, { self: true, optional: true });