Angular Enterprise
Injection de dépendances en Angular
- Cours
- 20 pratiques
- 6 min de lecture
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 leur 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 ou une directive peut obtenir des services de son propre injecteur, des injecteurs de ses composants ancêtres, de l'injecteur de sa route ou de son NgModule parent, ou de l'injecteur racine (créé par bootstrapApplication ou le module d'application).
Les dépendances peuvent être demandées via les paramètres du constructeur ou avec la fonction inject(), disponible depuis Angular 14 et désormais l'approche recommandée dans le code Angular moderne.
Organiser les dépendances
Tree-shakable servicessont possibles depuis Angular version 6 en ajoutantprovidedIn: 'root'(ou'platform') directement dans le décorateur@Injectable()du service : 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 tableauproviders: []debootstrapApplication(ou du module core), soit en utilisant la syntaxeprovidedIn: 'root'dans leur décorateur@Injectable(), puis ils peuvent être utilisés partout (lazy ou non) sans les mettre dans le tableauproviders: []d'un autre module.Shared services(partagés par plusieurs modules lazy ou initiaux) : si vous placez vos services dans le tableauprovidersdu module partagé et que vous importez ensuite ce module, comme prévu, dans plusieurs modules lazy, alors chaque module chargé en lazy obtiendra sa propre instance du service et non le singleton attendu. Cela est dû au fait que les modules chargés en lazy ont leurs propres injecteurs. La solution la plus simple estprovidedIn: 'root'. Sinon vous pouvez utiliser l'interfaceModuleWithProviderset créer deux méthodes statiques,forRoot()/forChild(), afin que les providers ne soient enregistrés qu'une seule fois alors que le module est importé dans des modules initiaux et lazy. Cette solution est utilisée par le framework Angular lui-même pour le serviceRouterduRouterModule.Feature services(route ou module lazy) peuvent être scopés à cette feature en supprimant leprovidedIn: 'root'du décorateur@Injectable()et en les ajoutant au tableauproviders: []de la route lazy ou du module lazy à la place. De cette manière le lazy loading du service est explicitement fait avec la feature lazy, mais sachez qu'avecprovidedIn: 'root'le compilateur Angular va également faire ceci si et uniquement si votre service est utilisé uniquement dans cette feature lazy.Component servicespeuvent être scopés à ce composant en supprimant leprovidedIn: 'root'du décorateur@Injectable()et en les ajoutant au tableauproviders: []du composant. Le service sera disponible dans tous les composants enfants, les enfants de vue et les enfants de contenu. En plus desproviders, vous pouvez ajouter un tableauviewProviderssi vous souhaitez scoper le même token (avec une classe différente) uniquement à la vue du composant lui-même, par conséquent les enfants de contenu (ng-content) utiliseront le service du tableauprovidersdéfini en premier.Platform servicespartagés entre plusieurs applications ou Angular Elements. Vous pouvez utiliserprovidedIn: 'platform'afin de rendre un service disponible entre plusieurs applications ou Angular Elements.Singleton servicespeuvent être créés à l'aide deprovidedIn: '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 tableauprovidersd'un module comme c'était le cas dans les versions d'Angular inférieures à la v6.Non singleton servicespouvaient être créés en utilisantprovidedIn: 'any'afin de créer des services isolés (contrairement à un singleton) pour chaque injecteur chargé en lazy. Cette option est dépréciée depuis Angular 15, fournissez plutôt le service dans le composant ou la route qui a besoin de sa propre instance.
Configurer les dépendances
- Ensuite, vous devez comprendre les différentes configurations d'injection que vous pouvez faire, en fait vous pouvez configurer l'injection avec différents types d'objets : une classe, un objet ou une simple valeur, une fabrique et même plus. Il vous sera obligatoire d'utiliser le mécanisme d'
InjectionTokensi le type n'a pas de représentation à l'exécution, par exemple une interface, sinon vous pouvez passer directement votre classe sansInjectionToken. - class:
{ provide: MyService, useClass: MyService }// Il est également possible d'utiliser un raccourci :MyService. - value:
{ provide: 'MY_CONST', useValue: 'https://angular.dev' }//'MY_CONST'peut être déclaré comme une chaîne de caractères sansInjectionToken. - value:
{ provide: MY_CONST, useValue: 'https://angular.dev' }//MY_CONSTpeut être déclaré commeInjectionToken<string>. - value:
{ provide: MY_CONFIG, useValue: { value: 'https://angular.dev' } }//MY_CONFIGdoit être déclaré commeInjectionToken<MyInterface>car une interface n'a pas de représentation à l'exécution. - factory:
{ provide: MY_OBS, deps: [DOCUMENT], useFactory: doThingFactory }//MY_OBSdoit être déclaré commeInjectionToken<Observable<string>>etdoThingFactoryest 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 : avecuseFactory, ce n'est pas tree-shakable, vous devez déclarer le provider manuellement et vous pouvez facilement basculer entre différentes implémentations via une autre fonctionuseFactory. Avec l'optionfactoryd'InjectionToken, c'est tree-shakable, le token est automatiquement fourni en root mais vous pouvez toujours modifier l'implémentation en déclarant un autre provider pour ce token dans un tableauproviders. - existing:
{ provide: MY_TOKEN, useExisting: forwardRef(() => MyDirective) }//MY_TOKENdevient un alias de l'instance deMyDirective. En général,forwardRefest utilisé lorsqu'une classe est référencée avant sa définition et il permet aussi parfois de casser facilement une dépendance circulaire.
Décorer les dépendances
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 dans le tableau deps d'une fabrique, et les mêmes options existent pour la fonction inject(), par exemple inject(MyService, { optional: true }).
- par défaut : injecter sans décorateur, en remontant la hiérarchie des injecteurs...
- self : injecter en utilisant uniquement le provider du composant lui-même (
@Self()ou{ self: true }) - skipSelf : injecter en ignorant le provider du composant lui-même (
@SkipSelf()ou{ skipSelf: true }) - optionnel : injecter s'il est fourni, sinon retourner
null(@Optional()ou{ optional: true }) - host : injecter en regardant d'abord le composant lui-même et, s'il n'y est pas trouvé, chercher l'injecteur jusqu'à son composant hôte (
@Host()ou{ host: true }). Attention il existe des cas particuliers avec les directives et la projection de contenu.
En savoir plus sur la DI en Angular
En savoir plus sur l'injection de dépendances dans la documentation Angular officielle.