Introducing Angular Modules - Root Module

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 introduces @NgModule and its most oft used features when creating a root module. Angular Modules can also assist with separation of features, and eager and lazy loading of those features. I'll explore these in future posts, but first we must grasp the basics and create a simple root module.

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.

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

Simple Root Module

We use an Angular Module as the starting point for our applications. When we decorate a class with @NgModule we are telling Angular important information about this Angular Module's role.

// app.module.ts
@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
})
export class AppModule { }

First, we declare that the AppComponent belongs to the AppModule by setting the AppComponent in the declarations property. Our AppComponent may have a selector of my-app. When Angular sees my-app in the template it will now recognize it.

Every app starts somewhere. By convention our apps start at AppModule. This is our root module and thus we advise Angular that when it creates AppModule at start that we want to bootstrap AppComponent into the DOM.

We import modules that our Angular Modules will use. In the example above we import the BrowserModule which registers important application service providers for running in the browser. BrowserModule includes built-in directives like NgIf and NgFor. This means we can use these built-in directives in any of this module's component templates.

Bootstrapping our App

We start our applications using main.ts, file by convention (see the style guide).

The example below shows how we will bootstrap AppModule using the browser platform. Again, by convention, we name the AppModule file app.module.ts.

// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

This uses the Just in Time (JIT) compilation. We can use Ahead of Time (AOT), but that is a topic for a future post.

Declaring

We create custom components, directives and pipes in our application. We can tell Angular that this module will use these by declaring the in the @NgModule declarations property.

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

Imports

We imported BrowserModule, which itself imports the CommonModule which contains the common built-in directives such as NgIf and NgFor. By importing BrowserModule, we in turn get what CommonModule has to offer too.

What if we want to use form functionality such as ngModel or http? We can import those modules, too. This makes these features available in or AppModule.

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

Now that we have imported FormsModule, we can use ngModel in our templates. When we use http to get or post data since we imported HttpModule.

Providers

We often have services that we want to share within our app. How do we allow all of our components to use a service? We use Angular Modules.

We can add add providers to the application's root dependency injector in our AppModule. Since our AppModule is our root module, this will make the services available everywhere in the application.

Some ideal examples of services we want to make available across the board are be a user profile service or a logging service. Our app also shows a list of vehicles, so we might have a VehicleService to retrieve those using http (which we can use because we imported the HttpModule).

// logger.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class LoggerService {
  log = (msg: string) => {
    console.log(msg);
  };
}
// vehicle.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class VehicleService {
  getVehicles = () => [
    { id: 1, name: 'X-Wing Fighter' },
    { id: 2, name: 'Tie Fighter' },
    { id: 3, name: 'Y-Wing Fighter' }
  ];
}

When we provide these services to the AppModule they are available to our declared components.

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

When a component tries to inject a service in its constructor, it is asking Angular to find that service. In this sample with our root app module, Angular first looks in this component's (VehicleListComponent) injector for our service. Since we did not provide the service in the @Component decorator's providers property, Angular keeps going up the component tree until it finds the service. We never provided it in a component, so it ultimately looks in its root injector and finds our service. If the service has already been instantiated, we get that service instance. If not, a new instance is created. This is how we locate and inject services.

Providers are perhaps one of the most involved and challenging Angular Module concepts. For a general rule of thumb, provide services you intend to be shared across your app, as a single multi-use instance, in the app root module.

What Else?

Using these concepts we can get started with Angular Modules. But when our app grows, how do we know when and where to create new Angular Modules for our feature areas? How do they related to one another? How do we make sure services are instantiated as singletons or multiple instances? How do we decide if code should be eager or lazy loaded? These are topics for future posts now that we know basic building blocks of Angular Modules.

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