Understanding NgModule

I have been studying Angular 2 for almost a year now.  One of the big changes that came out this summer in a release candidate was introduction to NgModule, a new concept in Angular 2.  In this post I just want to document my understanding of how this concept works, mostly from a perspective of a .NET developer.  You can read a very lengthy, good explanation here.  I want to provide a cliff-notes version for myself though.

 

At a high level I see NgModule as a basic unit of packaging of your application.  In my mind it is very similar to a namespace or an assembly in C# / .NET.  There are some hard rules to NgModule, and I want to list them first.

  • You must package all your artifacts, components, services, pipes and directives into modules in order to use them.
  • One artifact can belong to exactly one module.  I do not necessarily agree with this design, but it is what it is.
  • In order to use any components that belong to one module in a different module, you must import a module you are interested in.  On the other hand, all functionality you want  available outside of a module, must be exported from it first.  In a sense you build a module dependency tree by importing one module into another.

 

Built-in modules

There are certain built-in modules you have to use.

  • BrowserModule.  Import this in the root module of your application. It is needed to bootstrap / start your app.  You should not need it anywhere else.   import { BrowserModule } from “@angular/platform-browser”;
  • CommonModuleThis module contains all common components that come prebuilt with Angualr 2, such as NgIf or NgFor or prebuilt pipes.  It is very likely you have to import this into every module you create yourself.  import { CommonModule } from “@angular/common”;
  • HttpModule.  As the name implies, this encapsulates all HTTP functionality.  If you are building services to expose your data, you need to import this module in everyone of your own modules that make HTTP calls via services.  import { HttpModule } from “@angular/http”;
  • FormsModule.  Import this when you want to use Angular forms.  There is also related ReactiveFormsModule , which is used to build reactive forms.  import { FormsModule, ReactiveFormsModule }   from “@angular/forms”;
  • RourterModule.  As the name implies, you will need to import this in every module that relies on Angular routing to navigate between screens.  I only had to import this into the root module of my application.  I also had to import it into files where I define routes, but so that I can create routes via RouterModule.forRoot or RouterModule.forChild calls. import { RouterModule } from “@angular/router”;

You have to decorate your module class with a NgModule decorator to essentially make it a module.  My module classes do not have any code in them, just class name and NgModule decorator.  Here is an example

@NgModule({ imports: [CommonModule, HttpModule, ContactRouting, FormsModule, ReactiveFormsModule, SharedModule], declarations: [ContactList, ContactListItem, Contacts, ContactEdit], providers: [Configuration, HttpModule, ContactService], exports: [ContactList] }) export class ContactModule { }

NgModule decorator has these main properties

  • imports: those are your dependencies, typically other modules or route declarations that your module relies upon.
  • declarations: there are components, pipes, directives that belong to this module
  • providers: these are services or other injectables that belong to this module
  • exports: these are components and other items, such as directives or pipes, that you want visible outside of this module, as they provide some useful functionality. In my modules these seem to coincide with entry components.  The only exception is shared modules.
  • entryComponents: these are components that I want the user to navigate to via routing typically, i.e. starting points or screens.  Documentation is not very clear on this fact, but this is what I found from my experiments.
  • bootstrap: I only use this in the root module of the app, which will contain my shell page component, root visual for my app.

There are other properties, but I have not used them.

I found that I declare two types of modules.  One is my root application module, an entry point into my app. The other type is feature module, a group of functionality.

Here is an example of my root module

import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { ContactTypesModule } from "./contactTypes/contactTypesModule"; import { ContactModule } from "./contacts/contactModule"; import { App } from "./app"; import { RouterModule } from "@angular/router"; import { About } from "./about"; import { HomePage } from "./home"; import { PageNotFound } from "./pageNotFound"; import { DynamicPage, Option1, Option2 } from "./dynamicPage"; import { AppModuleRoutes } from "./routes"; @NgModule({ imports: [BrowserModule, AppModuleRoutes, ContactTypesModule, ContactModule, RouterModule], declarations: [App, About, HomePage, PageNotFound, DynamicPage, Option1, Option2], providers: [], entryComponents: [Option1, Option2], bootstrap: [App] }) export class AppModule { }

 

And here is an example of a feature modules

import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { HttpModule } from "@angular/http"; import { FormsModule } from "@angular/forms"; import { ContactTypesList } from "./contactTypesList"; import { ContactTypeListItem } from "./contactTypeListItem"; import { ContactTypeService } from "./contactTypeService"; import { ContactTypeEdit } from "./contactTypeEdit"; import { ContactTypes } from "./contactTypes"; import { ContactTypesRouting } from "./contactTypeRoutes"; import { Configuration } from "../configuration"; import { SharedModule } from "../sharedModule"; @NgModule({ imports: [CommonModule, HttpModule, FormsModule, ContactTypesRouting, SharedModule], declarations: [ContactTypesList, ContactTypeListItem, ContactTypes, ContactTypeEdit], providers: [Configuration, HttpModule, ContactTypeService], exports: [ContactTypesList], }) export class ContactTypesModule { }

I also found that in  my “learning app” I had a need for a shared module, which is essentially a feature module.  The reason for that is that a single component can only be declared in one module.  So in order to share, you have to create a module for this purpose.  If you want to share the same component in a number of ways, you have have to import your shared module in another module, but then export it back our, combining one shared module with another shared module.  Here is an example.

import { Shorten } from "./commonPipes/shorten"; import { MakeLarge } from "./commonDirectives/makeLarge"; import { Progress } from "./progress"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; @NgModule({ imports: [CommonModule], declarations: [Shorten, MakeLarge, Progress], exports: [Shorten, MakeLarge, Progress], }) export class SharedModule { }

Ok I think I can still follow my train of thought, which is a good sign.

In this post I tried to briefly and concisely explain modularity in Angular 2.  Enjoy and correct me if needed.

Leave a Reply

Your email address will not be published. Required fields are marked *