Modules
Services
HTTP
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 }
a TypeScript class with @NgModule annotation
Identifies it's own components, directives, pipes, services
Makes some of them public
App module
Libraries
CoreModule
Feature
Feature
Shared
Angular, etc
App module
Libraries
CoreModule
Feature
Feature
Router
With lazy loading
Angular, etc
Shared
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({
declarations: [ AppComponent ],
imports: [ BrowserModule ],
exports: [ ... ]
providers: [ ... ],
bootstrap: [ AppComponent ]
})
declarations - Declare components, directives, and pipes to make them privately available in this module.
@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
@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
@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({ ... })
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 { }
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`;
}
}
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
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 { }
provideIn
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
import { Injectable } from '@angular/core';
import { UserModule } from './user.module';
@Injectable({
providedIn: UserModule,
})
export class UserService {
}
The example above shows the preferred way to provide a service in a module. This method is preferred because it enables tree-shaking of the service if nothing injects it.
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) {}
}
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) {}
}
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 { }
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 { }
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;
}
}
1. Import it to the application module:
import { HttpClientModule } from '@angular/common/http'
imports: [
BrowserModule,
HttpClientModule
],
2. Inject HttpClient into your service:
constructor(private http: HttpClient) {
}
3. Use it:
public getUsers(): Observable<User[]> {
return this.http.get<User[]>(`${BASE_URL}`);
}
4. Call an API inside of your component:
this.usersService.getUsers().subscribe(
(response) => ...,
(error) => ...,
() => ...
)
Pass count and text fragment as a query params
return this.http.get<User[]>(`${BASE_URL}`, {
params: {textFragment, count}
});
Full get method description:
get(url: string, options?: {
headers?: HttpHeaders | {
[header: string]: string | string[];
};
observe?: 'events' | 'response' | 'body';
params?: HttpParams | {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'arraybuffer' | 'blob' | 'text' | 'json';
withCredentials?: boolean;
}): Observable<any>;
Create a new hero
return this.http.post<Hero>(`${BASE_URL}`, hero, httpOptions);
Full post method description:
post(url: string, body: any, options?: {
headers?: HttpHeaders | {
[header: string]: string | string[];
};
observe?: 'events' | 'response' | 'body';
params?: HttpParams | {
[param: string]: string | string[];
};
reportProgress?: boolean;
responseType?: 'arraybuffer' | 'blob' | 'text' | 'json';
withCredentials?: boolean;
}): Observable<any>
Handle error in the component:
public search(queryString: string): void {
this.usersWithParamsSubscription = this.usersService
.getUsersWithParams(queryString, this.countToLoad)
.subscribe(
(res: User[]) => {
this.users = res;
},
(error: HttpErrorResponse) => console.log(error)
)
}
Handle error in the service and retry:
public getUsersWithParams(textFragment: string, count: string): Observable<User[]> {
return this.http.get<User[]>(`${BASE_URL}`, { params: { textFragment, count } })
.pipe(
retry(4),
catchError(this.handleError)
);
}
Describe interceptor as a TypeScript class with @Injectable decorator:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Clone the request and replace the original headers with
// cloned headers, updated with the authorization.
const authReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer token')
});
// Pass the request to the next handler
return next.handle(authReq);
}
}
Provide your interceptor:
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
https://pazel.dev/how-to-keep-your-secrets-from-your-source-code-in-an-angular-project