Comprendre les modules Angular (NgModule)

Publié le

Nous avons traduit pour vous l’article Medium de notre formateur contributeur : comprendre les modules Angular.

La première structure de base que vous croiserez dans Angular, ce sont les NgModules. Mais c’est aussi la plus subtile et la plus complexe, à cause de différences de portée. La documentation officielle a mis à disposition toute une foire aux questions à propos des NgModules, et malgré tout cela reste compliqué à enseigner lors des formations que j’anime, cela perturbe les débutants, alors j’ai décidé de tout résumer dans cet article.

Pourquoi des NgModules ?

Angular CLI le fait automatiquement, mais la première chose qu’il faut faire dans Angular est de charger le NgModule racine :

Un NgModule sert à enregistrer tout ce que vous créez dans Angular, et les grouper ensemble (un peu comme un package Java ou un namespace en PHP / C#).

Il y a deux types de structures principales :

  • « declarations » pour les choses que vous utilisez dans vos templates : principalement les composants (~ vues : les classes qui affichent les données), mais aussi des directives et les filtres (pipes) ;
  • « providers » pour les services (~ modèles : les classes récupérant et traitant les données).

Note : depuis Angular 6, il n’est plus nécessaire de déclarer les services dans un NgModule.

NgModule et portée / visibilité

La confusion commence avec le fait que les déclarations et les providers n’ont pas la même portée :

  • les déclarations / composants sont en portée locale (~ visibilité privée),
  • les providers / services sont (généralement) en portée globale (~ visibilité publique).

Cela signifie que les composants que vous déclarez seront seulement utilisables dans le module en cours. Si vous en avez besoin à l’extérieur, dans un autre module, vous devez les exporter :

A l’inverse, les services que vous fournissez sont généralement disponibles / injectables n’importe où dans votre application, dans n’importe quel module.

Quand importer un NgModule ?

La différence de portée entre les composants et les services est importante à connaître, mais jusque là ça va encore. Les choses se compliquent car évidemment, comme dans n’importe quel framework ou application, vous n’allez pas avoir qu’un seul module, mais plusieurs. Angular lui-même est décomposé en plusieurs modules différents (core, common, http, etc.).

La deuxième chose essentielle que vous ferez dans un module Angular est donc d’importer les autres NgModules dont vous avez besoin.

Le problème, c’est que vous devez savoir pourquoi vous importer ces autres modules :

  • est-ce pour récupérer des composants, des directives ou des filtres (pipes) ?
  • ou est-ce pour récupérer des services ?

Pourquoi est-ce important ? Car étant donné la différence de portée entre les composants et les services :

  • si le module est importé pour des composants, il faudra le réimporter dans chaque module qui en a besoin ;
  • si le module est importé pour des services, il faudra l’importer une seule fois, dans le premier AppModule,

Sinon, vous aurez des erreurs liées à des composants non disponibles, car vous avez oublié d’importer à nouveau leur module.

Ou si vous importez un module de services plusieurs fois, cela peut générer des erreurs dans des situations avancées comme le lazy-loading.

Quand importer les principaux modules Angular ?

Une bonne connaissance des modules Angular est donc nécessaire, pour savoir combien de fois vous avez besoin de les importer. Voici un résumé.

Modules à importer à chaque fois que vous en avez besoin :

  • CommonModule (templating Angular : bindings, *ngIf, *ngFor…), sauf dans le premier AppModule, car il est déjà inclus dans le BrowserModule
  • FormsModule / ReactiveFormsModule
  • MatXModule et autres modules d’UI (comme PrimeNg)
  • tout autre module vous donnant des composants, directives ou filtres (pipes)

Modules à importer seulement une fois

  • HttpClientModule
  • BrowserAnimationsModule ou NoopAnimationsModule
  • tout autre module vous fournissant uniquement des services.

C’est pour ces raisons qu’Angular CLI importe automatiquement le CommonModule quand vous créez un nouveau module.

NgModules mixtes

Cela peut encore se compliquer : comment gérer un module qui fournit à la fois des composants et des services ?

Vous en connaissez un : le RouterModule. Il vous donne accès à un composant (<router-outlet>) et une directive (routerLink), mais aussi à des services (ActivatedRoute pour récupérer les paramètres de l’URL, Router pour naviguer, etc.).

Heureusement, cela est géré en interne par le module. Angular CLI pré-génère automatiquement les fichiers de routing, mais vous aurez peut-être remarqué qu’il y a une légère différence entre le routing du premier AppModule et celui des sous-modules.

Pour le AppModule :

Pour les sous-modules :

Pourquoi ? Car la première fois dans l’AppModule, forRoot() va fournir les composants et les services du router. Mais les fois suivantes dans les sous-modules, forChild() fournira seulement les composants (sans refournir à nouveau les services, ce qui ne serait pas bon).

Lazy-loaded modules

Dernière complication : si vous chargez des modules à la demande (lazy-loading), ce qui est désormais facile via Angular CLI.

Comme il s’agira d’un bundle et d’un module différents, chargés seulement sur demande (par défaut), cela ne fera pas tout à fait partie de l’espace global de votre application.

Pour les composants, cela ne change rien : vous devez importer à nouveau le CommonModule ou votre SharedModule, comme dans n’importe quel sous-module.

En revanche, pour les services, il y a une différence :

  • vous aurez toujours accès aux services déjà fournis par l’application (comme Http ou vos propres services) ;
  • par contre, les services fournis dans votre module chargé à la demande seront seulement disponibles dans la portée de ce module-ci, et non pas dans toute l’application.

Conclusion

Votre arbre d’imports (qui n’est pas exactement le même que vos répertoires) devrait ressembler à ça :

Architecture d’un projet Angular

Vous voulez savoir comment structurer vos propres modules dans une application Angular ? Lisez l’article suivant.

Découvrez notre formation Angular, animée par l’auteur de cet article !


6 réponses à “Comprendre les modules Angular (NgModule)”

  1. Article très eductif sur l’apprentissage d’angular Js. Bonne continuation

  2. article très intéressant, merci beaucoup. Selon ton experience, combien as-tu en moyenne de composants par module pour ne pas que ça soit trop chargé dans les projets? Merci pour ta réponse 🙂

  3. Ce n’est pas tant une question de quantité que de responsabilité : le module doit gérer un seul aspect de l’application, ne pas mélanger des rôles. Le nombre de composants et services nécessaires pour cela est très variable, tout dépend de quoi il s’agit.

  4. Serait-il possible de faire du lazy-loaded modules et ajouet le provider pour avoir accès constamment accès à ce service au delà de la portée du module?

    @NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
    providers: [SomeService]
    })

  5. Si vous souhaitez qu’un service soit disponible partout, c’est qu’il doit faire partie d’un module lui-même chargé normalement dans le AppModule ou le CoreModule (non lazy-loadé). Sinon, c’est qu’il y a un problème d’organisation des fonctionnalités.

  6. Probablement le meilleur article lu sur le sujet, merci. Je vous aiderais bien a faire quelques ajustements sur la version anglaise pour qu’elle soit plus lisible 🙂 mais c’est du detail. Merci d’avoir explique le fonctionnement de l’import de modules et de la portee des components et des services, c’est tout a fait clair et je peux l’instruire a mes developeurs.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.