Angular 2 give us the chance to organize our application in modules.
Briefly, a module is a library where there may be components, directives and pipes and define a block of functionalities; Angular 2 libraries itself are modules, like FormsModule, HttpModule.
One cool behaviour of the modules is the ability to be loaded eagerly or lazyly through the router; this is a huge improvement, because we are able to load a module only when required and not at the startup of the application; we’ll see that the router help us in this phase.
What we’ll do in this example is define two commons modules, shared in the application; then we’ll define the feature modules, that are modules with a specific purphose; finally we’ll import them in the root module of the application.
Shared modules
In every application we have some stuff that we want to share, libraries such as translations, bootstrap, rxjs, and angular modules like FormsModule and even CommonsModule.
For these, the better choice is define a shared module that import these libraries:
import { NgModule, ModuleWithProviders, Optional, SkipSelf } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { CommonModule } from "@angular/common"; import { TranslateModule } from "ng2-translate"; import { ModalModule, DatepickerModule } from 'ng2-bootstrap/ng2-bootstrap'; import "rxjs/add/observable/throw"; import "rxjs/add/observable/forkJoin"; import "rxjs/add/operator/catch"; import "rxjs/add/operator/debounceTime"; import "rxjs/add/operator/distinctUntilChanged"; import "rxjs/add/operator/map"; import "rxjs/add/operator/switchMap"; import "rxjs/add/operator/toPromise"; @NgModule ({ imports: [ FormsModule, CommonModule, ModalModule.forRoot(), DatepickerModule.forRoot(), ], exports: [ FormsModule, CommonModule, TranslateModule, ModalModule, DatepickerModule ] }) export class SharedModule {}
What we done is import the bunch of modules that we want to include, then we defined the list of imported/exported modules.
If importing the modules is obvious, we can’t says the same thing about the export; we need to export the modules in order to make them shared; otherwise the others modules couldn’t access to these libraries.
Another behaviour that deserves a mention is the forRoot static method of the ModalModule and the DatepickerModule.
Angular 2 has a convention, so when a module has singleton services needs to implement a static method forRoot; otherwise we could provide these services, but thus the other modules could import these shared module and instantiate them.
The second module that we implement is the CoreModule, that contains the singleton services of the application:
import { NgModule, ModuleWithProviders, Optional, SkipSelf } from "@angular/core"; import { ToastModule } from "ng2-toastr/ng2-toastr"; import { SearchService } from "./search.service"; import { AlertService } from "./alert.service"; let options: any = { autoDismiss: true, positionClass: 'toast-bottom-right', }; @NgModule ({ imports: [ ToastModule.forRoot(options) ] }) export class CoreModule { constructor (@Optional() @SkipSelf() parentModule: CoreModule) { if (parentModule) { throw new Error('CoreModule is already loaded. Import it in the AppModule only'); } } static forRoot(): ModuleWithProviders { return { ngModule: CoreModule, providers: [ SearchService, ToastModule, AlertService ] }; } }
First of all, we import the ToastModule as singleton, by calling the forRoot method and passing the configuration as a parameter.
As explained above, because this module provide singleton services, have to implement a static forRoot method.
The constructor of the module class deserves an additional comment; this module must be loaded only from the root module, so to disallow the import from a feature module, we check this and eventually we throw an exception.
Feature modules
We can now implement our feature modules, that have everyone a specific purphose in the application.
For example, a CustomerModule will look like this:
import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; import { HttpModule } from "@angular/http"; import { SharedModule } from "../shared/shared.module"; import { CityModule } from "../city/city.module"; import { CustomerComponent } from "./customer.component"; import { CustomerDetailComponent } from "./customer.detail.component"; import { SearchCustomersPipe } from "./searchCustomers.pipe"; import { CustomerService } from "./customer.service"; @NgModule ({ imports: [ HttpModule, SharedModule, CityModule, RouterModule.forChild([ { path: "", component: CustomerComponent }]) ], exports: [ RouterModule ], declarations: [ CustomerComponent, CustomerDetailComponent, SearchCustomersPipe ], providers: [ CustomerService ] }) export class CustomerModule {}
You can notice that the module import the shared module defined above.
The module deals to import the modules that needs and define with the static method forChild the root paths for the module.
Then declare the components and provide the services that belongs to the module.
App module
The last module is the root module, that is the startup module.
This module imports the shared modules of the application and define the feature modules:
import { NgModule } from "@angular/core"; import { HttpModule } from "@angular/http"; import { RouterModule } from "@angular/router"; import { BrowserModule } from "@angular/platform-browser"; import { TranslateModule } from "ng2-translate"; import { SharedModule } from "./shared/shared.module"; import { CoreModule } from "./core/core.module"; import { AppComponent } from "../app/app.component"; import { HeaderComponent } from "./header.component"; @NgModule ({ imports: [ BrowserModule, HttpModule, TranslateModule.forRoot(), CoreModule.forRoot(), SharedModule, RouterModule.forRoot([ { path: "", redirectTo: "customers", pathMatch: "full" }, { path: "customers", loadChildren: "app/customer/customer.module#CustomerModule" }, { path: "invoices", loadChildren: "app/invoice/invoice.module#InvoiceModule" }, ])], exports: [ RouterModule ], declarations: [ AppComponent, HeaderComponent ], bootstrap: [ AppComponent ] }) export class AppModule {}
As you can see, the CoreModule is imported with the forRoot method, which means that is singleton as well as the RouterModule.
We defined the root paths of the router by loading the concerning modules as childrens; thus they will be loaded lazy.
So, these modules and the dependent modules will be loaded the first time only once the routing path will be fired; this is a awesome feature of Angular 2.
Here the full Angular2 project.
Leave a Reply