The @NgModule is a new decorator that has recently been added in Angular 2. NgModule defines an Angular Module, which (from the official docs) are defined as "Angular Modules help organize an application into cohesive blocks of functionality."

This post is the second in a series that introduce @NgModule and its role in creating Angular 2 apps. In this post I'll discuss some motivations for creating multiple modules, some decisions we have to make regarding them, and how routing fits into modules.

Angular Modules Series

  1. Introducing NgModule and the Root Module
  2. Routing Module
  3. Feature Modules
  4. Shared/Core Modules
  5. Eager and Lazy Loading

I'm currently updating my Angular 2 First Look course on Pluralsight for this and other new topics. Be sure to check back later this Fall.

One Root Module

When we create an Angular 2 app, we define a root module. We learned about this in the previous post. This root module is defined with @NgModule and works quite well for small apps.

// app.module.ts
@NgModule({
  imports: [BrowserModule, FormsModule, HttpModule],
  declarations: [
    AppComponent,
    VehicleListComponent,
    VehicleSelectionDirective,
    VehicleSortingPipe
  ],
  providers: [
    LoggerService,
    VehicleService,
    UserProfileService
  ],
  bootstrap: [AppComponent],
})
export class AppModule { }

Our root module declares our components, pipes and directives so Angular can recognize them in our templates. Our root module imports common features from the Angular 2 BrowserModule, FormsModule, and HttpModule. This allows us to use common features like ngIf and ngFor, ngModel for forms, and the Http service in our app. We're pretty set right now on basic functionality.

Our root module also provides a few custom services. Because we do this in the root module, anyone in our current app can use these.

We could continue to add more components and features to this root module, and it would work. But at some point we may want to organize our app by related features. Perhaps we have a vehicles feature, an characters feature, a login feature, and a reusable widgets feature. Each of these feature areas may have multiple components and it might make sense to keep them together.

Why Create More Modules?

But Why? Why keep the features together? Why encapsulate the features? One reason is organization and readability.

A more technical reason is that we may want to make a feature promotable. For example a we could make a feature that has custom widgets such as a CalendarComponent and ButtonComponent. We may want to use these across the entire app and intend to use them in other apps. Making a feature area for these widgets makes a lot of sense in this case.

Another reason for encapsulating feature areas is to help keep the starting app lean and fast. When the app starts we may want to only send the parts of the app that are needed to crank it up and are the most commonly used pieces. That may not include all feature areas. The ones we want to load up front could be eagerly loaded while the others could be lazily loaded as needed.

All of these are made easier by @NgModule.

Expanding Our App with Features

Let's warp speed forward a bit and our app now has some of these features. We would likely start by putting all of these in the root module, and it might end up looking something like the following.

// app.module.ts
@NgModule({
  imports: [BrowserModule, FormsModule, HttpModule],
  declarations: [
    // Root Feature
    AppComponent,
    NavComponent,

    // Widgets Feature
    ButtonComponent,
    DateComponent,
    
    // Character Feature
    CharactersListComponent,
    CharactersDetailComponent,

    // Vehicle Feature
    VehicleListComponent,
    VehicleSelectionDirective,
    VehicleSortingPipe
  ],
  providers: [
    LoggerService,
    VehicleService,
    UserProfileService
  ],
  bootstrap: [AppComponent],
})
export class AppModule { }

Notice our declarables got quite a bit larger. That's ok ... now let's step aside and look at another requirement of most apps: routing.

Routing in a Module

Our app has many features and we want to use routing to navigate between our components. We also have a NavComponent that the allows the user to navigate. We're going to need to add routing logic to our app.

// app.routing.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CharactersComponent } from './characters/characters.component.ts';
import { VehiclesComponent } from './vehicles/vehicles.component.ts';

const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'vehicles' },
  { path: 'vehicles', component: VehiclesComponent },
  { path: 'characters', component: CharactersComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule { }

export const routingComponents = [VehiclesComponent, CharactersComponent];

Here we created an app.routing.ts file that exports AppRoutingModule which is ... wait for it ... an Angular module!

Notice how we are explicitly referencing the components in the routes? These are currently set up for eager loading. We can make them load on demand (aka lazy loading) ... we'll learn more about that in a future post.

We just discovered another reason to create modules. We can now import this module into our root module.

// app.module.ts
//...
import { AppRoutingModule, routingComponents } from './app.routing';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  declarations: [AppComponent, routingComponents],
  providers: [VehicleService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Importing the routing module we created also gives us the routing features we need. That forRoot() function returned a module, which is imported by AppRoutingModule, which in turn is imported in the root module.

The forRoot() function should only be used for creating the root modules' routes. When we get to child modules and routes, we'll use other techniques that we'll see in later posts.

Here is a simple example of an app with a root module and a routing module.

Routing Module

More Modules?

We just took the first steps to creating more modules, for our features. Along the way we created a routing module and imported it. If we want to organize our app by feature, promote our widgets, and load features as we use them it may benefit us to encapsulate the features. We'll do this by creating more modules in the next post.

Ultimate Angular 2 Workshop

If you want to learn more about Angular 2 and NgModule, come join Dan Wahlin and I at the Ultimate Angular 2 Workshop on October 6 and 7, in Ft Lauderdale, Florida

This is an immersive two day Angular 2 hands-on workshop that is custom tailored to help you jump start your skills with Angular 2.

Register now