Ngmodules: Ngmodules Help Organize An Application Into Cohesive Blocks of Functionality
Ngmodules: Ngmodules Help Organize An Application Into Cohesive Blocks of Functionality
Ngmodules: Ngmodules Help Organize An Application Into Cohesive Blocks of Functionality
An NgModule is a class adorned with the @NgModule decorator function. @NgModule takes a
metadata object that tells Angular how to compile and run module code. It identifies the module's
own components, directives, and pipes, making some of them public so external components can
use them. @NgModule may add service providers to the application dependency injectors. And
there are many more options covered here.
Before reading this page, read the The Root Module page, which introduces NgModules and the
essentials of creating and maintaining a single root AppModule for the entire application.
Table of Contents
Angular modularity
Declarations
Providers
Imports
Resolve conflicts
Feature modules
Shared modules
Live examples
This page explains NgModules through a progression of improvements to a sample with a "Tour
of Heroes" theme. Here's an index to live examples at key moments in the evolution of the
sample:
The companion NgModule FAQs cookbook offers answers to specific design and
implementation questions. Read this page before reading those FAQs.
Angular modularity
Modules are a great way to organize an application and extend it with capabilities from external
libraries.
Many Angular libraries are modules (such as FormsModule, HttpModule, and RouterModule).
Many third-party libraries are available as NgModules (such as Material Design, Ionic,
AngularFire2).
NgModules consolidate components, directives, and pipes into cohesive blocks of functionality,
each focused on a feature area, application business domain, workflow, or common collection of
utilities.
Modules can also add services to the application. Such services might be internally developed,
such as the application logger. Services can come from outside sources, such as the Angular
router and Http client.
Modules can be loaded eagerly when the application starts. They can also be lazy loaded
asynchronously by the router.
An NgModule is a class decorated with @NgModule metadata. The metadata do the following:
Declare which components, directives, and pipes belong to the module.
Make some of those classes public so that other component templates can use them.
Import other modules with the components, directives, and pipes needed by the components in
this module.
Provide services at the application level that any application component can use.
Every Angular app has at least one module class, the root module. You bootstrap that module to
launch the application.
The root module is all you need in a simple application with a few components. As the app
grows, you refactor the root module into feature modules that represent collections of related
functionality. You then import these modules into the root module.
Later in this page, you'll read about this process. For now, you'll start with the root module.
The AppModule from the QuickStart seed on the Setup page is as minimal as possible:
src/app/app.module.ts (minimal)
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
The @NgModule decorator defines the metadata for the module. This page takes an intuitive
approach to understanding the metadata and fills in details as it progresses.
The metadata imports a single helper module, BrowserModule, which every browser app must
import.
src/app/app.component.ts (minimal)
@Component({
selector: 'my-app',
template: '<h1>{{title}}</h1>',
})
export class AppComponent {
title = 'Minimal NgModule';
}
Bootstrapping in main.ts
You launch the application by bootstrapping the AppModule in the main.ts file.
Angular offers a variety of bootstrapping options targeting multiple platforms. This page
describes two options, both targeting the browser.
In the first, dynamic option, the Angular compiler compiles the application in the browser and
then launches the app.
src/main.ts (dynamic)
Consider the static alternative which can produce a much smaller application that launches faster,
especially on mobile devices and high latency networks.
In the static option, the Angular compiler runs ahead of time as part of the build process,
producing a collection of class factories in their own files. Among them is the
AppModuleNgFactory.
The syntax for bootstrapping the pre-compiled AppModuleNgFactory is similar to the dynamic
version that bootstraps the AppModule class.
src/main.ts (static)
Because the entire application was pre-compiled, Angular doesn't ship the Angular compiler to
the browser and doesn't compile in the browser.
The application code downloaded to the browser is much smaller than the dynamic equivalent
and it's ready to execute immediately. The performance boost can be significant.
Both the JIT and AOT compilers generate an AppModuleNgFactory class from the same
AppModule source code. The JIT compiler creates that factory class on the fly, in memory, in the
browser. The AOT compiler outputs the factory to a physical file that is imported here in the
static version of main.ts.
In general, the AppModule should neither know nor care how it is bootstrapped.
Although the AppModule evolves as the app grows, the bootstrap code in main.ts doesn't
change. This is the last time you'll look at main.ts.
src/app/highlight.directive.ts
If you ran the app now, Angular wouldn't recognize the highlight attribute and would ignore it.
You must declare the directive in AppModule.
Import the HighlightDirective class and add it to the module's declarations like this:
declarations: [
AppComponent,
HighlightDirective,
],
Add a component
Refactor the title into its own TitleComponent. The component's template binds to the
component's title and subtitle properties like this:
src/app/title.component.html
src/app/title.component.ts
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
})
export class TitleComponent {
@Input() subtitle = '';
title = 'Angular Modules';
}
Rewrite the AppComponent to display the new TitleComponent in the <app-title> element,
using an input binding to set the subtitle.
src/app/app.component.ts (v1)
@Component({
selector: 'my-app',
template: '<app-title [subtitle]="subtitle"></app-title>'
})
export class AppComponent {
subtitle = '(v1)';
}
Angular won't recognize the <app-title> tag until you declare it in AppModule. Import the
TitleComponent class and add it to the module's declarations:
declarations: [
AppComponent,
HighlightDirective,
TitleComponent,
],
Service providers
Modules are a great way to provide services for all of the module's components.
The Dependency Injection page describes the Angular hierarchical dependency-injection system
and how to configure that system with providers at different levels of the application's
component tree.
A module can add providers to the application's root dependency injector, making those services
available everywhere in the application.
Many applications capture information about the currently logged-in user and make that
information accessible through a user service. This sample application has a dummy
implementation of such a UserService.
src/app/user.service.ts
@Injectable()
/** Dummy version of an authenticated user service */
export class UserService {
userName = 'Sherlock Holmes';
}
The sample application should display a welcome message to the logged-in user just below the
application title. Update the TitleComponent template to show the welcome message below the
application title.
src/app/title.component.html
Update the TitleComponent class with a constructor that injects the UserService and sets the
component's user property from the service.
src/app/title.component.ts
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
})
export class TitleComponent {
@Input() subtitle = '';
title = 'Angular Modules';
user = '';
constructor(userService: UserService) {
this.user = userService.userName;
}
}
You've defined and used the service. Now to provide it for all components to use, add it to a
providers property in the AppModule metadata:
src/app/app.module.ts (providers)
providers: [ UserService ],
src/app/title.component.html (ngIf)
<p *ngIf="user">
<i>Welcome, {{user}}</i>
<p>
Although AppModule doesn't declare NgIf, the application still compiles and runs. How can that
be? The Angular compiler should either ignore or complain about unrecognized HTML.
Angular does recognize NgIf because you imported it earlier. The initial version of AppModule
imports BrowserModule.
src/app/app.module.ts (imports)
imports: [ BrowserModule ],
Importing BrowserModule made all of its public components, directives, and pipes visible to the
component templates in AppModule.
More accurately, NgIf is declared in CommonModule from @angular/common.
CommonModule contributes many of the common directives that applications need, including
ngIf and ngFor.
BrowserModule imports CommonModule and re-exports it. The net effect is that an importer of
BrowserModule gets CommonModule directives automatically.
Many familiar Angular directives don't belong to CommonModule. For example, NgModel and
RouterLink belong to Angular's FormsModule and RouterModule respectively. You must import
those modules before you can use their directives.
To illustrate this point, you'll extend the sample app with ContactComponent, a form component
that imports form support from the Angular FormsModule.
The ContactComponent presents a "contact editor," implemented with Angular forms in the
template-driven form style.
The following sample imports the FormsModule from @angular/forms because the
ContactComponent is written in template-driven style. Modules with components written in the
reactive style import the ReactiveFormsModule.
src/app/app.component.ts (template)
template: `
<app-title [subtitle]="subtitle"></app-title>
<app-contact></app-contact>
`
Form components are often complex. The ContactComponent has its own ContactService and
custom pipe (called Awesome), and an alternative version of the HighlightDirective.
3.
6. <div class="form-group">
7. <label for="name">Name</label>
9. [(ngModel)]="contact.name"
13. </div>
14. </div>
15. <br>
19. </form>
In the middle of the component template, notice the two-way data binding [(ngModel)].
ngModel is the selector for the NgModel directive.
Although NgModel is an Angular directive, the Angular compiler won't recognize it for the
following reasons:
Now [(ngModel)] binding will work and the user input will be validated by Angular forms,
once you declare the new component, pipe, and directive.
The application won't compile until you declare the contact component, directive, and pipe.
Update the declarations in the AppModule accordingly:
src/app/app.module.ts (declarations)
declarations: [
AppComponent,
HighlightDirective,
TitleComponent,
AwesomePipe,
ContactComponent,
ContactHighlightDirective
],
There are two directives with the same name, both called HighlightDirective.
To work around this, create an alias for the contact version using the as JavaScript import
keyword.
import {
HighlightDirective as ContactHighlightDirective
} from './contact/highlight.directive';
This solves the immediate issue of referencing both directive types in the same file but leaves
another issue unresolved. You'll learn more about that issue later in this page, in Resolve
directive conflicts.
Provide the ContactService
You have to provide that service somewhere. The ContactComponent could provide it, but then
the service would be scoped to this component only. You want to share this service with other
contact-related components that you'll surely add later.
src/app/app.module.ts (providers)
Now you can inject ContactService (like UserService) into any component in the application.
Application-scoped providers
Architecturally, the ContactService belongs to the Contact business domain. Classes in other
domains don't need the ContactService and shouldn't inject it.
You might expect Angular to offer a module-scoping mechanism to enforce this design. It
doesn't. NgModule instances, unlike components, don't have their own injectors so they can't
have their own provider scopes.
In practice, service scoping is rarely an issue. Non-contact components can't accidentally inject
the ContactService. To inject ContactService, you must first import its type. Only Contact
components should import the ContactService type.
Read more in the How do I restrict service scope to a module? section of the NgModule FAQs
page.
app
app.component.ts
app.module.ts
highlight.directive.ts
title.component.(html|ts)
user.service.ts
contact
awesome.pipe.ts
contact.component.(css|html|ts)
contact.service.ts
highlight.directive.ts
The selectors of the two directives both highlight the attached element with a different color.
6. constructor(el: ElementRef) {
7. el.nativeElement.style.backgroundColor = 'gold';
8. console.log(
10. }
11. }
Both directives are declared in this module so both directives are active.
When the two directives compete to color the same element, the directive that's declared later
wins because its DOM changes overwrite the first. In this case, the contact's
HighlightDirective makes the application title text blue when it should stay gold.
The issue is that two different classes are trying to do the same thing.
It's OK to import the same directive class multiple times. Angular removes duplicate classes and
only registers one of them.
But from Angular's perspective, two different classes, defined in different files, that have the
same name are not duplicates. Angular keeps both directives and they take turns modifying the
same HTML element.
At least the app still compiles. If you define two different component classes with the same
selector specifying the same element tag, the compiler reports an error. It can't insert two
components in the same DOM location.
To eliminate component and directive conflicts, create feature modules that insulate the
declarations in one module from the declarations in another.
Feature modules
This application isn't big yet, but it's already experiencing structural issues.
The root AppModule grows larger with each new application class.
There are conflicting directives. The HighlightDirective in the contact re-colors the work
done by the HighlightDirective declared in AppModule. Also, it colors the application title
text when it should color only the ContactComponent.
The app lacks clear boundaries between contact functionality and other application features.
That lack of clarity makes it harder to assign development responsibilities to different teams.
A feature module is a class adorned by the @NgModule decorator and its metadata, just like a root
module. Feature module metadata have the same properties as the metadata for a root module.
The root module and the feature module share the same execution context. They share the same
dependency injector, which means the services in one module are available to all.
You boot the root module to launch the app; you import a feature module to extend the app.
A feature module can expose or hide its implementation from other modules.
While you can do everything within the root module, feature modules help you partition the app
into areas of specific interest and purpose.
A feature module collaborates with the root module and with other modules through the services
it provides and the components, directives, and pipes that it shares.
In the next section, you'll carve the contact functionality out of the root module and into a
dedicated feature module.
It's easy to refactor the contact material into a contact feature module.
src/app/contact/contact.module.ts
4.
6.
7. import
11.
12. @NgModule({
17. })
You copy from AppModule the contact-related import statements and @NgModule properties that
concern the contact, and paste them into ContactModule.
You import the FormsModule because the contact component needs it.
Modules don't inherit access to the components, directives, or pipes that are declared in other
modules. What AppModule imports is irrelevant to ContactModule and vice versa. Before
ContactComponent can bind with [(ngModel)], its ContactModule must import FormsModule.
You also replaced BrowserModule by CommonModule, for reasons explained in the Should I
import BrowserModule or CommonModule? section of the NgModule FAQs page.
You declare the contact component, directive, and pipe in the module declarations.
You export the ContactComponent so other modules that import the ContactModule can include
it in their component templates.
All other declared contact classes are private by default. The AwesomePipe and
HighlightDirective are hidden from the rest of the application. The HighlightDirective can
no longer color the AppComponent title text.
Return to the AppModule and remove everything specific to the contact feature set.
Delete the FormsModule from the imports list (AppComponent doesn't need it).
Then import the ContactModule so the app can continue to display the exported
ContactComponent.
Here's the refactored version of the AppModule along with the previous version.
3.
4. /* App Root */
5. import
10.
12. import
14.
15. @NgModule({
20. })
Improvements
It's simpler:
o No FormsModule import.
o No contact-specific declarations.
o No ContactService provider.
o No HighlightDirective conflict.
Try this ContactModule version of the sample.
Examine and download the complete source for this version from the live example. /
downloadable example
The app has three feature modules: Contact, Hero, and Crisis.
The new AppComponent template has a title, three links, and a <router-outlet>.
template: `
<app-title [subtitle]="subtitle"></app-title>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
<a routerLink="heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
The <app-contact> element is gone; you're routing to the Contact page now.
src/app/app.module.ts (v3)
3.
4. /* App Root */
9.
12.
13. /* Routing Module */
15.
16. @NgModule({
17. imports: [
18. BrowserModule,
19. ContactModule,
20. AppRoutingModule
21. ],
25. })
Some file names bear a .3 extension that indicates a difference with prior or future versions. The
significant differences will be explained in due course.
The module still imports ContactModule so that its routes and components are mounted when
the app starts.
The module does not import HeroModule or CrisisModule. They'll be fetched and mounted
asynchronously when the user navigates to one of their routes.
The significant change from version 2 is the addition of the AppRoutingModule to the module
imports. The AppRoutingModule is a routing module that handles the app's routing concerns.
App routing
src/app/app-routing.module.ts
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
The router is the subject of the Routing & Navigation page, so this section skips many of the
details and concentrates on the intersection of NgModules and routing.
The first route redirects the empty URL (such as http://host.com/) to another route whose
path is contact (such as http://host.com/contact).
The contact route isn't defined here. It's defined in the Contact feature's own routing module,
contact-routing.module.ts. It's standard practice for feature modules with routing
components to define their own routes. You'll get to that file in a moment.
The remaining two routes use lazy loading syntax to tell the router where to find the modules:
A lazy-loaded module location is a string, not a type. In this app, the string identifies both the
module file and the module class, the latter separated from the former by a #.
RouterModule.forRoot
The forRoot static class method of the RouterModule with the provided configuration and
added to the imports array provides the routing concerns for the module.
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
The returned AppRoutingModule class is a Routing Module containing both the RouterModule
directives and the dependency-injection providers that produce a configured Router.
src/app/app.module.ts (imports)
imports: [
BrowserModule,
ContactModule,
AppRoutingModule
],
src/app/contact/contact-routing.module.ts (routing)
@NgModule({
imports: [RouterModule.forChild([
{ path: 'contact', component: ContactComponent }
])],
exports: [RouterModule]
})
export class ContactRoutingModule {}
This time you pass the route list to the forChild method of the RouterModule. The route list is
only responsible for providing additional routes and is intended for feature modules.
forRoot and forChild are conventional names for methods that deliver different import values to
root and feature modules. Angular doesn't recognize them but Angular developers do.
Follow this convention if you write a similar module that has both shared declarables and
services.
1. @NgModule({
4. providers: [ ContactService ]
5. })
6. export class ContactModule { }
Now that you navigate to ContactComponent with the router, there's no reason to make it public.
Also, ContactComponent doesn't need a selector. No template will ever again reference this
ContactComponent. It's gone from the AppComponent template.
The lazy-loaded HeroModule and CrisisModule follow the same principles as any feature
module. They don't look different from the eagerly loaded ContactModule.
The HeroModule is a bit more complex than the CrisisModule, which makes it a more
interesting and useful example. Its file structure is as follows:
hero
hero-detail.component.ts
hero-list.component.ts
hero.component.ts
hero.module.ts
hero-routing.module.ts
hero.service.ts
highlight.directive.ts
This is the child routing scenario familiar to readers of the Child routing component section of
the Routing & Navigation page. The HeroComponent is the feature's top component and routing
host. Its template has a <router-outlet> that displays either a list of heroes (HeroList) or an
editor of a selected hero (HeroDetail). Both components delegate to the HeroService to fetch
and save data.
Yet another HighlightDirective colors elements in yet a different shade. In the next section,
Shared modules, you'll resolve the repetition and inconsistencies.
src/app/hero/hero.module.ts (class)
@NgModule({
imports: [ CommonModule, FormsModule, HeroRoutingModule ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
HighlightDirective
]
})
export class HeroModule { }
Shared modules
The app is shaping up. But it carries three different versions of the HighlightDirective. And
the many files cluttering the app folder level could be better organized.
Add a SharedModule to hold the common components, directives, and pipes and share them
with the modules that need them.
src/app/src/app/shared/shared.module.ts
4.
7.
8. @NgModule({
9. imports: [ CommonModule ],
13. })
If you review the application, you may notice that many components requiring SharedModule
directives also use NgIf and NgFor from CommonModule and bind to component properties with
[(ngModel)], a directive in the FormsModule. Modules that declare these components would
have to import CommonModule, FormsModule, and SharedModule.
You can reduce the repetition by having SharedModule re-export CommonModule and
FormsModule so that importers of SharedModule get CommonModule and FormsModule for free.
As it happens, the components declared by SharedModule itself don't bind with [(ngModel)].
Technically, there is no need for SharedModule to import FormsModule.
SharedModule can still export FormsModule without listing it among its imports.
SharedModule exists to make commonly used components, directives, and pipes available for
use in the templates of components in many other modules.
The TitleComponent is used only once by the AppComponent. There's no point in sharing it.
While many components share the same service instances, they rely on Angular dependency
injection to do this kind of sharing, not the module system.
Several components of the sample inject the UserService. There should be only one instance of
the UserService in the entire application and only one provider of it.
UserService is an application-wide singleton. You don't want each module to have its own
separate instance. Yet there is a real danger of that happening if the SharedModule provides the
UserService.
Do not specify app-wide singleton providers in a shared module. A lazy-loaded module that
imports that shared module makes its own copy of the service.
Instead, gather them in a single CoreModule that you import once when the app starts and never
import anywhere else.
src/app/src/app/core/core.module.ts
1. import {
2. ModuleWithProviders, NgModule,
4.
6.
9. @NgModule({
14. })
15. export class CoreModule {
16. }
You're importing some extra symbols from the Angular core library that you're not using yet.
They'll become relevant later in this page.
The @NgModule metadata should be familiar. You declare the TitleComponent because this
module owns it and you export it because AppComponent (which is in AppModule) displays the
title in its template. TitleComponent needs the Angular NgIf directive that you import from
CommonModule.
CoreModule provides the UserService. Angular registers that provider with the app root
injector, making a singleton instance of the UserService available to any component that needs
it, whether that component is eagerly or lazily loaded.
Why bother?
This scenario is clearly contrived. The app is too small to worry about a single service file and a
tiny, one-time component.
A TitleComponent sitting in the root folder isn't bothering anyone. The root AppModule can
register the UserService itself, as it does currently, even if you decide to relocate the
UserService file to the src/app/core folder.
Real-world apps have more to worry about. They can have several single-use components (such
as spinners, message toasts, and modal dialogs) that appear only in the AppComponent template.
You don't import them elsewhere so they're not shared in that sense. Yet they're too big and
messy to leave loose in the root folder.
Apps often have many singleton services like this sample's UserService. Each must be
registered exactly once, in the app root injector, when the application starts.
While many components inject such services in their constructorsand therefore require
JavaScript import statements to import their symbolsno other component or module should
define or re-create the services themselves. Their providers aren't shared.
We recommend collecting such single-use classes and hiding their details inside a CoreModule.
A simplified root AppModule imports CoreModule in its capacity as orchestrator of the
application as a whole.
Cleanup
Having refactored to a CoreModule and a SharedModule, it's time to clean up the other modules.
A trimmer AppModule
3.
4. /* App Root */
6.
7. /* Feature Modules */
10.
13.
14. @NgModule({
15. imports: [
16. BrowserModule,
17. ContactModule,
18. CoreModule,
19. AppRoutingModule
20. ],
23. })
24. export class AppModule { }
A little smaller because many src/app/root classes have moved to other modules.
Stable because you'll add future components and providers to other modules, not this one.
A trimmer ContactModule
3.
7.
8. @NgModule({
12. })
By convention, the forRoot static method both provides and configures services at the same
time. It takes a service configuration object and returns a ModuleWithProviders, which is a
simple object with the following properties:
The root AppModule imports the CoreModule and adds the providers to the AppModule
providers.
More precisely, Angular accumulates all imported providers before appending the items listed in
@NgModule.providers. This sequence ensures that whatever you add explicitly to the
AppModule providers takes precedence over the providers of imported modules.
src/app/core/user.service.ts (constructor)
src/app/core/core.module.ts (forRoot)
src/app//app.module.ts (imports)
imports: [
BrowserModule,
ContactModule,
CoreModule.forRoot({userName: 'Miss Marple'}),
AppRoutingModule
],
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
Call forRoot only in the root application module, AppModule. Calling it in any other module,
particularly in a lazy-loaded module, is contrary to the intent and can produce a runtime error.
Remember to import the result; don't add it to any other @NgModule list.
You could hope that no developer makes that mistake. Or you can guard against it and fail fast by
adding the following CoreModule constructor.
The constructor tells Angular to inject the CoreModule into itself. That seems dangerously
circular.
The injection would be circular if Angular looked for CoreModule in the current injector. The
@SkipSelf decorator means "look for CoreModule in an ancestor injector, above me in the
injector hierarchy."
If the constructor executes as intended in the AppModule, there is no ancestor injector that could
provide an instance of CoreModule. The injector should give up.
By default, the injector throws an error when it can't find a requested provider. The @Optional
decorator means not finding the service is OK. The injector returns null, the parentModule
parameter is null, and the constructor concludes uneventfully.
It's a different story if you improperly import CoreModule into a lazy-loaded module such as
HeroModule (try it).
Angular creates a lazy-loaded module with its own injector, a child of the root injector.
@SkipSelf causes Angular to look for a CoreModule in the parent injector, which this time is the
root injector. Of course it finds the instance imported by the root AppModule. Now
parentModule exists and the constructor throws the error.
Conclusion
You made it! You can examine and download the complete source for this final version from the
live example.
Now that you understand NgModules, you may be interested in the companion NgModule FAQs
page with its ready answers to specific design and implementation questions.