Angular
Modules
Services
ES6 Modules
function baz() {
// Some logic here
}
function qux() {
// Another magic here
}
export { baz, qux }
export function foo() {
// Some logic here
}
export function bar() {
// Another magic here
}
import { foo, bar } from 'helpers1.js';
import { baz, qux } from 'helpers2.js';
const appComponent = {};
foo();
bar();
export { appComponent, baz, qux }
@NgModule
a TypeScript class with @NgModule annotation
Identifies it's own components, directives, pipes, services
Makes some of them public
Why to use @NgModule?
- Better business logic organization
- Re-usability
- Maintainability
- Performance (lazy-loading)
Types of @NgModules
- Library modules - Angular (BrowserModule, FormsModule, HttpModule, RouterModule, ...), 3rd-party modules
- Features - related business logic that can be packaged into a single concern.
- Core - module that contains root logic (mostly services).
- Widget/Shared - components you use everywhere. Loading spinners, social media icons, etc.
- Routing - used to manage routes. Should not declare anything.
App module
Project modules structure
Libraries
CoreModule
Feature
Feature
Shared
Angular, etc
App module
Project modules structure
Libraries
CoreModule
Feature
Feature
Router
With lazy loading
Angular, etc
Shared
@NgModule structure
AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
@NgModule metadata
@NgModule({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
exports: [ ... ]
providers: [ ... ],
bootstrap: [ AppComponent ]
})
-
declarations - Declare components, directives, and pipes to make them privately available in this module.
- imports - This is where you import other modules.
- exports - Makes the declared view public so they can be used by other modules.
- providers - Defines services that can be injected into this module’s views.
- bootstrap - The component used to launch the app, the AppComponent
exports, imports...
@NgModule({
declarations: [ a1, a2, a3 ],
exports: [ a1, a2 ]
})
Module A
@NgModule({
imports: [ ModuleA ]
declarations: [ b1, b2 ],
exports: [ b1 ]
})
Module B
@NgModule({
imports: [ ModuleB ]
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
AppModule
Scope
a1, a2, a3
a1, a2, b1, b2
b1, AppComponent
exports, imports...
@NgModule({
declarations: [ a1, a2, a3 ],
exports: [ a1, a2 ]
})
Module A
@NgModule({
imports: [ ModuleA ]
declarations: [ b1, b2 ],
exports: [ b1, ModuleA ]
})
Module B
@NgModule({
imports: [ ModuleB ]
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
AppModule
Scope
a1, a2, a3
a1, a2, b1, b2
a1, a2,
b1, AppComponent
providers...
@NgModule({
providers: [ s1, s2 ]
})
Module A
@NgModule({
imports: [ ModuleA ]
providers: [ s3 ],
})
Module B
@NgModule({
imports: [ ModuleB ],
providers: [ s4 ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
AppModule
Root Injector
s1, s2, s3, s4
s1, s2, s3
Scope
s1, s2
@NgModule Configuration
@NgModule({ ... })
export class GreeingModule {
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: GreetingModule,
providers: [
{ provide: UserServiceConfig, useValue: config }
]
};
}
}
@NgModule({
imports: [
BrowserModule,
ContactModule,
GreetingModule.forRoot({userName: 'Miss Marple'}),
AppRoutingModule
],
declarations: [
AppComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }
Services & DI
Class without DI
export class Car {
public engine: Engine;
public wheels: Wheel[];
private readonly description: 'without DI';
constructor() {
this.engine = new Engine();
this.wheels = new Array(4).fill(null).map(() => new Wheel());
}
drive() {
return `${this.description}: car with ` +
`${this.engine.volume} engine and ` +
`${this.wheels[0].material} wheels`;
}
}
Class with DI
export class CarWithDI {
private readonly description: 'with DI';
constructor(public engine: Engine, public wheels: Wheels) {}
drive() {
return `${this.description}: car with ` +
`${this.engine.volume} engine and ` +
`${this.wheels[0].material} wheels`;
}
}
let car = new CarWithDI(getInstance(Engine), getInstance(Wheels))
getInstance - gets existing instance of creates a new one
Services
- Contains any value, function, or feature that an app needs
- Should do something specific and do it well
- Singleton
Services
Must be provided in order to use it
import { Injectable } from '@angular/core';
@Injectable()
export class FooService {
bar() {
// Some magic happens
}
}
import { Component } from '@angular/core';
import { FooService } from '../services/foo-service';
@Component({ ... })
export class FooComponent {
constructor(private fooService: FooService) {}
}
import { NgModule } from '@angular/core';
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [ FooService ],
bootstrap: [ FooComponent ]
})
export class FeatureModule { }
Dependency Providers
import { NgModule } from '@angular/core';
import { FooService } from 'services/foo-service';
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
FooService,
{ provide: 'SecondInstance', useClass: FooService }
],
bootstrap: [ FooComponent ]
})
export class FeatureModule { }
import { Component, Inject } from '@angular/core';
import { FooService } from '../services/foo-service';
@Component({ ... })
export class FooComponent {
constructor(private fooService: FooService,
@Inject('SecondInstance') private secondFooService: FooService) {}
}
useExisting
import { NgModule } from '@angular/core';
import { FooService } from '../services/foo-service';
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
FooService,
{ provide: 'SecondToken', useExisting: FooService }
],
bootstrap: [ FooComponent ]
})
export class FeatureModule { }
import { Component, Inject } from '@angular/core';
import { FooService } from '../services/foo-service';
@Component({ ... })
export class FooComponent {
constructor(@Inject('SecondToken') private secondFooService: FooService) {}
}
UseValue & UseFactory
import { NgModule } from '@angular/core';
import { FooService } from 'services/foo-service';
import { AnotherImplementationService} from 'services/another-service';
function myFactory(IS_PROD: boolean) {
return IS_PROD ? new FooService() : new AnotherImplementationService();
}
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
{ provide: 'IS_PROD', useValue: true },
{ provide: FooService, useFactory: myFactory, deps: ['IS_PROD']}
],
bootstrap: [ FooComponent ]
})
export class FeatureModule { }
InjectionToken
import { NgModule, InjectionToken } from '@angular/core';
import { FooService } from 'services/foo-service';
import { AnotherImplementationService} from 'services/another-service';
const IS_PROD = new InjectionToken<boolean>('isProd');
function myFactory(IS_PROD: boolean) {
return IS_PROD ? new FooService() : new AnotherImplementationService();
}
@NgModule({
declarations: [ ... ],
imports: [ ... ],
providers: [
{ provide: IS_PROD, useValue: true },
{ provide: FooService, useFactory: myFactory, deps: ['IS_PROD']}
],
bootstrap: [ FooComponent ]
})
export class FeatureModule { }
Optional Dependencies
import { Injectable, Optional } from '@angular/core';
import { FooService } from './data-service';
@Injectable()
export class MyDataService {
public data: any;
constructor(@Optional() private dataService: DataService) {
this.data = this.dataService ? this.dataService.getData() : 'local';
}
getData() {
return this.data;
}
}
Useful links
- https://angular.io/tutorial/toh-pt4
- https://angular.io/guide/architecture-modules
- https://angular.io/guide/ngmodules
- https://angular.io/guide/ngmodule-faq
- https://angular.io/guide/architecture-services
- https://angular.io/guide/dependency-injection
- https://angular.io/guide/dependency-injection-providers
- https://angular.io/guide/dependency-injection-in-action
- https://www.freelancermap.com/freelancer-tips/12255-forroot-forchild-angular
Q & A
Angular. Modules & Services
By Pavel Razuvalau
Angular. Modules & Services
- 866