Article
Développement mobile
28.9.2017
Perturbation continuum temporel
6 minutes
Temps d'un café
Les modules Angular
Les modules Angular
Emilie Paillous
Note : Ce contenu a été créé avant que Fabernovel ne fasse partie du groupe EY, le 5 juillet 2022.

L’App Module

Le principal et premier module de l’application est l’AppModule. Avant découpage de l’application en différents modules, l’app module contient l’intégralité de l’application. Après découpage, son rôle est d’importer les modules fournis par Angular (BrowserModule, BrowserAnimationsModule, etc), puis chacun des sous-modules. On distingue 3 types de “sous modules” :

  • CoreModule : il est unique au sein de l’application. Il ne doit être importé que dans l’AppModule. Il contient essentiellement les services singletons communs à toute l’application (par exemple, un service AuthService qui permet d’accéder à l’utilisateur courant depuis n’importe quel endroit de l’application), ainsi que les composants utilisés seulement dans le template AppComponent (par exemple, le menu de l’application, la barre de navigation, etc.)
  • SharedModule : également unique au sein de l’application, il contient tous les composants communs à l’application. Il ne doit fournir aucun service (voir ici).
  • FeatureModule : comme son nom l’indique, il s’agit d’un module propre à une “fonctionnalité”. Dans notre cas, nous avons un FeatureModule par entrée du menu principal. Nous avons également un FeatureModule “admin” qui permet d’accéder à l’administration de l’application (seulement pour les utilisateurs autorisés). Ce découpage en FeatureModule permet aussi de séparer proprement le code pour que deux développeurs puissent travailler facilement en parallèle sur des fonctionnalités différentes sans avoir à modifier le fichier app-module.ts. Un FeatureModule peut fournir des services propre à son module, leur durée de vie sera alors la même que celle du module. Par exemple, si votre application propose un système de notifications, il est pertinent d’avoir un NotificationsModule regroupant le composant affichant la liste des notifications, le service correspondant, le composant de détail de notification, etc.

Ce tableau récapitulatif permet de savoir rapidement quel type de module correspond à une situation donnée.

En supposant avoir un module “articles” permettant de lister des articles, et un module “admin” gérant la partie privée de l’application, voici l’AppModule après avoir appliqué ce découpage :

import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; /* Routing Module */ import { AppRoutingModule } from './app-routing.module'; /* App Root */ import { AppComponent } from './app.component'; /* Feature Modules */ import { CoreModule } from './core/core.module'; import { ArticlesModule } from './articles/articles.module'; import { AdminModule } from './admin/admin.module'; /* Widget Modules */ import { SharedModule } from './shared/shared.module'; @NgModule({   imports: [ BrowserModule, BrowserAnimationsModule, SharedModule, AppRoutingModule, CoreModule, ArticlesModule, AdminModule    ],   declarations: [ AppComponent ],   bootstrap: [ AppComponent ] }) export class AppModule { }

Les modules de routing

En réalité, il existe un quatrième type de module, que le tutoriel propose de ranger dans la catégorie des features modules mais que nous préférons distinguer par souci de clarté : les modules de routing. Ces modules n’aspirent qu’à définir un ensemble de routes et de sous routes permettant de faire le lien entre une adresse URL et le composant à afficher. De plus, les modules de routing fournissent des “guards” régulant l’accès à une route. Voici un exemple :

const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: '**', redirectTo: '/login', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [AuthGuard] }) export class AppRoutingModule { }

Il s’agit du module de routing principal de l’application, l’AppRoutingModule. Dans ce module, on établit une route principale, la route /login accessible par tous les utilisateurs. Ce module fournit aussi le guard “AuthGuard”, permettant de vérifier qu’un utilisateur est authentifié.

Analysons maintenant le module ArticlesRouting, importé par le module ArticlesModule, lui-même importé par l’AppModule.

const routes: Routes = [   { path: 'articles', canActivate: [ AuthGuard ], children: [ { path: '', component: ArticlesListComponent } { path: ':id', component: ArticleDetailComponent } ]   } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class ArticlesRoutingModule { }

Ici, on définit :

  • la route /articles, qui affiche la liste des articles
  • la route /articles/:id qui permet d’afficher le détail d’un article

Ces deux routes ne sont accessibles que si l’utilisateur est authentifié, c’est à dire s’il passe le guard “AuthGuard” fournit par l’AppRoutingModule.

Enfin, voici le module AdminRouting permettant de définir les routes de la partie admin :

const routes: Routes = [ { path: 'admin', canActivate: [ AuthGuard, AdminGuard ], children: [ { path: 'users', component: UsersListComponent } ] } ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [AdminGuard] }) export class AdminRoutingModule { }

Ce module définit une unique route /admin/users permettant à un utilisateur authentifié et administrateur d’accéder à la liste des utilisateurs.

Le guard AdminGuard est fourni dans le module AdminRoutingModule puisqu’il n’est utilisé que dans le contexte de ce module.

Attention à bien utiliser imports: [RouterModule.forChild(routes)] et non imports: [RouterModule.forRoot(routes)] dans tous les features modules de l’application. Un appel à forRoot dans un composant enfant, de plus lazy-loadé, peut provoquer une erreur à l’execution.

Lazy Loading

Dans certains cas, il n’est pas nécessaire de charger toute l’application au démarrage. Par exemple, un utilisateur non admin n’aura jamais accès à la partie admin de l’application, il n’aura donc jamais besoin du module Admin. Pour éviter cet import superflu, on peut utiliser le lazy load afin qu’il ne soit chargé qu’au moment où il est effectivement nécessaire.

Pour chaque module que l’on souhaite lazy loader, il suffit de définir la route associée au module dans l’app routing. Supposons que l’on veuille lazy loader le module Admin, tout en conservant l’eagerload du module Article, voici ce que devient l’AppRoutingModule :

const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: 'admin', loadChildren: './admin/admin.module#AdminModule', canActivate: [AuthGuard, AdminGuard] }, { path: '**', redirectTo: '/login', pathMatch: 'full' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [AuthGuard, AdminGuard] }) export class AppRoutingModule { } AdminRoutingModule devient : const routes: Routes = [ { path: 'users', component: UsersListComponent} ]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule] }) export class AdminRoutingModule { }

A présent, c’est l’AppRoutingModule qui fournit l’AdminGuard pour éviter que le module soit chargé alors que les guards AuthGuard et AdminGuard ont empêché l’accès à cette route. De plus, l’AdminModule n’est plus importé dans l’AppModule. Il n’est donc chargé qu’au moment où on accède à la route /admin/users en tant qu’utilisateur connecté et administrateur.

Attention à ne pas abuser du lazy load. Si effectivement, le temps de chargement de la première page d’un utilisateur classique est diminué, un administrateur devra attendre un peu plus longtemps (le temps que le module se charge) pour accéder à la partie Admin. Il faut trouver un juste milieu entre utilisation du module et temps de chargement au démarrage.

No items found.
Pour aller plus loin :