Angular Modules

While a project is being developed, the more the features are numerous, the more difficult it is to comprehend the code. Angular offers a division through modules, that allows splitting the different features of the application. There is a strong correlation, or even a total equivalence between the different modules of an Angular application and the business features of the project.

The App Module

The main and first module of the application is the AppModule. Before dividing the application into different modules, the AppModule holds the entire application. After the division, its role is to import the modules provided by Angular (BrowserModule, BrowserAnimationsModule etc.), and then each of the submodules.

There are three types of “sub-modules”:

This summary table makes it possible to quickly know which type of module matches a given situation.

Let’s assume that you have an “articles” module allowing you to list articles, and an “admin” module managing the private area of the application. This is the AppModule after applying this breakdown:

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 { }

The routing modules

There is in fact a fourth type of module, which the tutorial proposes to classify in the category of features modules but which we prefer to distinguish for the sake of clarity: the routing modules. These modules only aim to define a set of routes and sub-routes allowing the link between a URL address and the component to be displayed. Moreover, the routing modules provide “guards” regulating access to a route. Here is an example:

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: '**', redirectTo: '/login', pathMatch: 'full' }
];

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

export class AppRoutingModule { }

This refers to the main routing module of the application, the AppRoutingModule. In this module, a main route is established, the /login route accessible to all users. This module also provides the AuthGuard, which verifies that a user is authenticated.

Let’s analyze now the ArticlesRouting module, imported by the ArticlesModule module, the latter having been imported by the 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 { }

Here, we are defining:

These two routes are only accessible if the user is authenticated, that is if he passes the AuthGuard provided by the AppRoutingModule.

Finally, this is the AdminRouting module that allows defining the routes of the admin panel:

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 { }

This module defines a single /admin/users route allowing an authenticated user and administrator to access the list of users.

The AdminGuard is provided in the AdminRoutingModule since it is only used in the context of this module.

Make sure you are using imports: [RouterModule.forChild(routes)] and not imports: [RouterModule.forRoot(routes)] in all the application’s feature modules. A call to forRoot in a child component, as well as lazy-loaded, can cause an error at execution.

Lazy Loading

In some cases, it is not necessary to load the entire application at startup. For example, a non-admin user will never have access to the admin panel of the application. Therefore, he will never need to use the Admin module. To avoid this useless import, you can use the lazy load so that it is loaded only when it is actually needed.

For each module you wish to lazy load, you just have to define the route associated with the module in the routing app. Let’s assume that we want to load the Admin module while keeping the Article module eager loaded. Here’s what the AppRoutingModule becomes:

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 becomes:

const routes: Routes = [
  { path: 'users', component: UsersListComponent}
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})

export class AdminRoutingModule { }

Now the AppRoutingModule is providing the AdminGuard to prevent the module from being loaded while the AuthGuard and AdminGuard have prevented access to that route. In addition, the AdminModule is no longer imported into the AppModule. It is therefore only loaded when accessing the /admin/users route as a logged in user and administrator.

Make sure you do not overuse the lazy load. If the loading time of the first page of a typical user is reduced, an administrator will have to wait a little longer (the time that the module loads) to access the Admin panel. A balance must be struck between the use of the module and the loading time at start-up.