image auteur
Lucas BENTO-VERSACE 11min • 05-04-2024

Angular - Les modules

Angular - Les modules

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 { }
app.module.ts

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
commande CLI de génération de module

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 { }
user.module.ts

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 { }
app.module.ts

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 { }
user.module.ts

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>  
app.component.html

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
Commande CLI génération de service

Cette commande crée le UserService :

1
2
3
4
5
6
7
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class UserService {
}
user.service.ts

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 {
}
user.service.ts

ou

1
2
3
4
5
import { Injectable } from '@angular/core';

@Injectable()
export class UserService {
}
user.service.ts
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 { }
user.module.ts

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 { }
shared.module.ts
  • 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.

NB : Il existe des pratiques plus modernes que les modules partagés pour partager du code entre les modules. Notamment l'approche SCAM ou les standalone components.

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 !

Auteur

image auteur
Lucas BENTO-VERSACE Alternant chez Codewise / Étudiant en développement web lucas.bentoversace@ynov.com
"Alternant chez Codewise, passioné par l'informatique et les technologies depuis mon plus jeune âge. J'ai découvert le monde du web et depuis je ne m'en lasse pas ! Fan de jeux vidéos, de sports et de musiques. "