Angular
Enterprise
./assets/drawing/angular-courses-dependency-injection.svg

Injection de dépendances en Angular

L'injection de dépendances (DI) est un modèle de conception dans lequel une classe demande des dépendances à des sources externes (les injecteurs) plutôt que de les créer elle-même. Angular a son propre framework DI qui aide à écrire des applications modulaires. Les dépendances ne sont rien d'autre que des services ou des objets avec un cycle de vie clair en fonction de leurs configuration.

Les injecteurs sont hérités, ce qui signifie que si un injecteur donné ne peut pas résoudre une dépendance, il demande à l'injecteur parent. Un composant / directives peuvent obtenir des services de son propre injecteur, des injecteurs de ses ancêtres composants, de l'injecteur de son parent NgModule ou de l'injecteur racine (créé à partir du module d'application).

Organiser les dépendences

  • Tree-shaking services sont possibles depuis Angular version six en ajoutant directement dans le service les éléments providedIn: 'root', 'platform', 'any', comme ça si le service n'est jamais injecté, il sera supprimé du bundle lors de la compilation.
  • Core services (non lazy) doivent être dans le dossier core et peuvent être déclarés soit dans le tableau providers: [] du module de base, soit en utilisant la syntaxe providedIn: 'root' de leurs décorateurs injectable puis peuvent être utilisés dans toutes sortes de modules (lazy ou non) sans les mettre dans le tableau de providers: [] de n'importe quel autre module.
  • Shared services (partagés par plusieurs modules lazy ou initial) si vous placez vos services dans le tableau de providers du module partagé et que vous les utilisez ensuite comme prévu un Shared services dans plusieurs modules lazy, alors chaque module chargé en lazy obtiendra son propre service par exemple et non le singleton intentionnel, en effet, cela est dû au fait que les modules chargés lazy ont leurs propres injecteurs. Pour résoudre ce problème, vous pouvez utiliser l'interface ModuleWithProviders et créer deux méthodes: forRoot/forChild avec les providers qui vont être importés dans des modules initial et lazy ou simplement dans plusieurs modules lazy. Cette solution est utilisée par le framework Angular lui-même pour résoudre le problème du Route service dans le module de Router.
  • Feature services (lazy module) peuvent être scopés à cette feature en supprimant le providedIn: 'root' du décorateur injectable et en les ajoutant au tableau providers: [] du module lazy à la place. De cette manière le lazy loading du service est explicitement fait avec le lazy module mais sachez que en utilisant providedIn: 'root' le compileur Angular va également faire ceci si et uniquement si votre service est utilisé uniquement dans ce lazy module.
  • Component services peuvent être scopés à ce composant en supprimant le providedIn: 'root' du décorateur injectable et en les ajoutant au tableau providers: [] du composant. Le service sera disponible dans tous les composants enfants, l'enfant de vue et l'enfant de contenu. En plus des providers, vous pouvez ajouter le tableau viewProviders si vous souhaitez étendre le même token (avec une classe différente) uniquement pour la vue du composant lui-même et, par conséquent, les enfants de contenu (ng-content) utiliseront le service du tableau des providers défini en premier.
  • Shared services entre plusieurs applications ou Angular Elements. Vous pouvez utiliser le providedIn: 'platform' afin de rendre un service disponible entre plusieurs applications ou Angular Elements.
  • Singleton services peuvent être créé à l'aide de provideIn: 'root', de cette façon, le service sera disponible à l'échelle de l'application en tant que singleton sans avoir besoin de l'ajouter au tableau de providers d'un module comme c'était le cas sur les versions de Angular inférieure a v5.
  • Non singleton services peuvent être créé en utilisant le paramètre providedIn: 'any' afin de créer des services isolés (contrairement à un singleton) pour chaque injecteur enfant.

Configurer les dépendences

  • Ensuite, vous devez comprendre les différentes configuration d'injection que vous pouvez faire, en fait vous pouvez configurer l'injection avec différents types d'objets, soit une classe, un objet ou une simple valeur, une fabrique et même plus. Il vous sera obligatoire d'utiliser le mécanisme d'InjectionToken si le type n'a pas de représentation à l'exécution, par exemple une interface sinon vous pouvez passer directement votre object sans InjectionToken.
  • class: { provide: MyService, useClass: MyService } // Il est également possible d'utiliser un raccourci: MyService.
  • value: { provide: 'MY_CONST', useValue: 'https://angular.io } // 'MY_CONST' peut être déclaré comme un String sans InjectionToken.
  • value: { provide: MY_CONST, useValue: 'https://angular.io } //MY_CONST peut être déclaré comme InjectionToken.
  • value: { provide: MyInterface, useValue: { value: 'https://angular.io} } // MyInterface doit être déclaré comme InjectionToken.
  • factory: { provide: MyObs, deps: [DOCUMENT], useFactory: doThingFactory } // MyObs doit être déclaré comme InjectionToken<Observable> et doThingFactory est une fonction qui renvoie l'observable. Vous pouvez également créer votre fabrique en utilisant le second argument d'InjectionToken. Veillez à bien comprendre la différence entre les deux: avec useFactory, ce n'est pas un Tree-shakable, vous devez déclarer le provider manuellement et vous pouvez facilement basculer entre différentes implémentations via une fonction useFactory directement. Avec la factory d'InjectionToken, il est Tree-shakable, le token est automatiquement provided en root mais vous pouvez toujours modifier l'implémentation en utilisant la fonction useExisting dans le provider array.
  • existing: { provide: MyInterface, useExisting: forwardRef(() => MyDirective) } // MyDirective implémente MyInterface et donc forwardRef renvoie une directive après la création de son instance. En général, forwardRef est utilisé lorsque l'injection est déclarée avant la définition du service ou aussi parfois lorsqu'il existe une dépendance circulaire, cela permet de la casser facilement.

Décorer les dépendences

Les décorateurs ci-dessous peuvent être utilisés pour configurer plus précisément le comportement d'injection. Ils peuvent être utilisés dans la méthode constructeur ou également dans le tableau deps tout en fournissant une fabrique.

  • par défaut: injecter sans décorateur, recherche de la hiérarchie des injecteurs ...
  • self: injecter en utilisant uniquement le fournisseur du composant lui-même (@Self())
  • skipSelf: injecter en sautant le fournisseur du composant lui-même (@SkipSelf())
  • optionnel: injecter si est fourni sinon retourner null (@Optional())
  • host: injectez en regardant d'abord le composant lui-même et s'il n'y est pas trouvé, il cherche l'injecteur jusqu'à son composant hôte. (@Hôte()). Attention il existe des cas particuliers avec directives et projection de contenu.

En savoir plus sur la DI en Angular

En savoir plus sur l'injection de dépendance dans la Documentation Angular officielle.

En apprendre plus sur Angular

Bonnes pratiques à connaître en développement angular .