Qu'est-ce que les modules dans Angular ? Dans cet article vous allez découvrir l'architecture modulaire et comment l'utiliser
Définition
Le framework Angular implémente une architecture modulaire pour concevoir les applications. Cela permet de mieux structurer le code et faciliter la réutilisation et le partage.
Un module permet de :
- Regrouper des composants, des services, des directives, des pipes etc... dans une même sous-application
- Définir les dépendances à ce module et tout ce qu'il comporte.
- Définir les visibilités des fichiers à travers le code de l'application.
Lorsque le navigateur charge un module, l'entièreté de ce qu'il contient se charge. Découper l'application en modules permet d'orchestrer le chargement de l'application afin de ne pas charger toute l'application dès l'affichage du premier composant. On obtient ainsi de meilleures performances.
Les modules sont aussi un moyen d'organiser une application et de l'étendre avec des fonctionnalités de bibliothèques externes.
NgModule
Un module est une classe marquée par le décorateur @NgModule
.
Il permet de configurer l’injecteur et le compilateur, et aide à l’organisation de votre application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Module racine
Lorsque que vous créez un projet angular, la CLI génère le AppModule (module racine).
Le fichier AppModule commence par des imports ES6, ensuite il est configuré via les paramètres de décorateur suivants :
- declarations
- imports
- providers
- bootstrap
Declarations : C’est dans cette metadata qu’on mentionne les composants, directives et pipes qui appartiendront au module.
Imports : On mentionne d’autres module à importer, afin de pouvoir utiliser leurs déclarables (pipes, directives, composants). C'est également ici qu'on place les composants standalone depuis Angular 16 au lieu de les lister dans la propriété déclarations
Providers : Cette metadata contient les fournisseurs de services que les composants du module peuvent utiliser.
Bootstrap : Il contient le composant de démarrage de l’application qui sera inséré dans le index.html. Il apparaît aussi dans le tableau déclarations. La propriété bootstrap ne peut s'utiliser que dans un seul module : le module racine.
Modules de fonctionnalité
Un module de fonctionnalité correspond tout simplement à un sous-module, par opposition au concept du module racine.
Bien que nous puissions tout déclarer dans le module racine, les modules de fonctionnalités vous aide à bien découper votre application.
Ils collaborent avec le module racine via les services, composants, directives et pipes qu’ils partagent.
Les modules de fonctionnalités sont donc tous les modules que vous créez et ceux implémentés de base par Angular comme : FormsModule
, HttpClientModule
, AppRoutingModule
etc.
Génerer un module
En entrant la commande suivante dans le répertoire racine du projet, vous créez un module de fonctionnalité :
1
ng generate module user
Cette commande amène la CLI à créer un dossier user
et un fichier user.module.ts contenant ce code :
1
2
3
4
5
6
7
8
9
10
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class UserModule { }
La structure d’un module est toujours la même, que ce soit un module racine ou de fonctionnalités.
Importer un module
Pour intégrer le module de fonctionnalités dans votre application, il est nécessaire d’informer le module racine : app.module.ts.
Pour l’importer, ajoutez le UserModule dans le tableau imports du fichier app.module.ts. comme ceci :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UserModule } from './user/user.module';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
UserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Maintenant le AppModule (module racine) connaît notre module de fonctionnalités (UserModule).
Les fournisseurs de services du module de fonctionnalités seront connus pour le AppModule. Cependant les NgModules n’exposent pas leurs composants par défaut.
Exporter un module
Lorsque que vous créez un composant dans votre module de fonctionnalités, il vous faut l'ajouter dans une liste d'exports pour le rendre disponible hors du module.
Dans le user.module.ts en dessous du tableau declarations des metadatas, ajoutez : exports, contenant le UserComponent.
1
2
3
4
5
6
7
8
9
10
11
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [],
imports: [
CommonModule
],
exports:[UserComponent]
})
export class UserModule { }
Maintenant si vous voulez afficher votre composant depuis le composant AppComponent (qui est hors du UserModule), il vous faudra importer le UserModule dans AppModule (le module qui contient AppComponent dans ses déclarations) puis appeler le sélecteur de votre component dans le fichier app.component.html
.
1
2
<h1>{{title}}</h1>
<app-user></app-user>
Fournir les services
Un fournisseur de services (provider) est une instruction au système d’injection de dépendance.
Ces dépendances sont des services Angular que vous créez ou importez.
Vous pouvez créer un service à l’aide de la CLI via cette commande :
1
ng generate service user
Cette commande crée le UserService :
1
2
3
4
5
6
7
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
Vous pouvez maintenant injecter le UserService n’importe où dans votre application.
Ou bien vous pouvez décider de réduire le scope au module concerné, soit en le déclarant dans le tableau providers
du module, soit en modifiant providedIn
du service à : 'NomDuModule' comme ceci :
1
2
3
4
5
6
7
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'UserModule',
})
export class UserService {
}
ou
1
2
3
4
5
import { Injectable } from '@angular/core';
@Injectable()
export class UserService {
}
1
2
3
4
5
6
7
8
9
10
11
12
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserService } from '../user.service';
@NgModule({
declarations: [],
imports: [
CommonModule
],
providers:[UserService],
})
export class UserModule { }
Module de partage
Lorsque vous séparez votre application en plusieurs modules de fonctionnalités, vous vous heurtez à un problème : dans quel module placer des composants ou des services qui seraient communs à plusieurs modules ?
La création de modules partagés vous permet de répondre à cette problématique. Vous pouvez placer des directives, des pipes et des composants, puis importer ce module là où vous en avez besoin dans d'autres parties de votre application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CustomerComponent } from './customer.component';
import { NewItemDirective } from './new-item.directive';
import { OrdersPipe } from './orders.pipe';
@NgModule({
imports: [ CommonModule ],
declarations: [ CustomerComponent, NewItemDirective, OrdersPipe ],
exports: [ CustomerComponent, NewItemDirective, OrdersPipe,
CommonModule, FormsModule ]
})
export class SharedModule { }
- Il importe CommonModule car le composant du module a besoin de directives communes.
- Il déclare et exporte le pipe utilitaire, la directive et les classes de composants.
- Il réexporte les CommonModule et FormsModule.
Tous les composants et modules exportés, seront automatiquement accessibles par tout autre module qui importe le SharedModule.
Bonnes pratiques
La convention la plus intuitive et la plus répandue est la suivante : 1 page routée de l'application = 1 module de fonctionnalités. C'est une convention qui fonctionne très bien et qui permet de mettre en place facilement le lazy-loading
Aussi, n'utilisez qu'un seul module partagé. Si vous avez besoin de gérer les partages de manière plus granulaire, renseignez-vous sur l'approche SCAM ou les standalone components.
Et pour finir, évitez tant que possible de surdécouper vos modules de fonctionnalités en plusieurs modules.
Conclusion
Voilà à peu près tout ce qu'il faut savoir sur les modules, l'architecture de votre application reste d'une importance capitale pour son évolution.
N'hésitez pas a laisser un commentaire !