Angular Basics IV - HTTP & Angular
In previous post TODO List Project - Backend Part I, we have introduced the basic knowledge of HTTP, RESTful services. In this post, we will mainly focus on HttpClient module, rxjs, Async pipe in Angular.
1. Angular HttpClient
To use HttpClient module in Angular, we can follow these steps:
- Import it in the root module. (
HttpClientModulefrom@angular/common/http) - Inject it in the constructor.
constructor(private http: HttpClient) {} - Use its
get/post/put/deletemethods. These methods are generic, which can directly transfer the Json data to given type.getTabs() { return this.http.get<TopMenu[]>(`${environment.baseUrl}/tabs`, { params: {icode: `${environment.icode}`}; }); } - The returned data is observable, which must be subscribed. If not, the request will not be sent.
ngOnInit() { this.service.getTabs().subscribe( tabs => { this.topMenus = tabs; } ); }
2. HttpInterceptor
Practice 1: A request interceptor
- Create a template for HttpInterceptor using
ng-http-interceptor. - Add a code in each request.
- Register the interceptor in
rootmodule.
This interceptor is like.
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { environment } from 'src/environments/environment';
@Injectable()
export class ParamInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const modified = req.clone({
setParams: { icode: environment.icode }
});
return next.handle(modified);
}
}
// in app.module.ts
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ParamInterceptor,
multi: true
}
]
Practice 2: A response interceptor
If we have a successful response, we want to pop something. We use pipe() method.
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse
} from '@angular/common/http';
import { tap } from 'rxjs/operators';
@Injectable()
export class NotificationInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
tap( (event: HttpEvent<any>) => {
if (event instanceof HttpResponse &&
event.status >= 200 && event.status < 300) {
console.log('pop toast ToT');
}
})
);
}
}
// in app.module.ts
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: NotificationInterceptor,
multi: true
}
]
3. rxjs - Reactive Extensions for Javascript
An important concept in rx is regarding event or data as stream.
For example,method
fromEvent()is used to inspect html event.
@ViewChild('inputRef') inputRef: ElementRef;
constructor() { }
ngOnInit() {
fromEvent(this.inputRef.nativeElement, 'input')
.subscribe( (ev: any) => {
console.log(ev.target.value);
})
}
// method fromEvent() is used to inspect html event.
In the above example, if we want to observe an input form, we first transform the input form into a stream named input.
Operators in rxjs
In rxjs, there are many useful operators, e.g. filter(), map,…
this.route.paramMap
.pipe(
filter(params => params.has('tabLink')),
map(params => params.get('tabLink'))
)
.subscribe(
tabLink => {
// this.selsectedTabLink = params.get('tabLink');
this.selsectedTabLink = tabLink;
this.cd.markForCheck();
});
async
We can use async to replace .subscribe. One benefit is we don’t need to clear subscriptions which may cause memory overflow. Also it makes code more concise.
selsectedTabLink$: Observable<string>;
imageSliders$: Observable<ImageSlider[]>;
channels$: Observable<Channel[]>;
this.selsectedTabLink$ = this.route.paramMap
.pipe(
filter(params => params.has('tabLink')),
map(params => params.get('tabLink'))
)
this.imageSliders$ = this.service.getBanners()
this.channels$ = this.service.getChannels()
<div *ngIf="(selsectedTabLink$ | async) === 'hot'; else others">
<app-image-slider [sliders]="imageSliders$ | async" #imageSlider></app-image-slider>
<span appGridItem *ngFor="let item of channels$ | async">
Practice: Count-down timer
<div> \{\{ countDown$ | async }} </div>
export class CountDownComponent implements OnInit {
@Input() startDate = new Date();
@Input() futureDate: Date;
private _MS_PER_SECOND = 1000;
countDown$: Observable<string>;
constructor() { }
ngOnInit() {
this.countDown$ = this.getCountDownObservable(this.startDate, this.futureDate);
}
private getCountDownObservable(startDate: Date, futureDate: Date) {
return interval(1000).pipe(
map(elapse => this.diffInSec(startDate, futureDate) - elapse),
takeWhile(gap => gap >= 0), // if failed then the whole stream is completed!
tap(val => console.log(val)), // print values in console
map(sec => ({ // ES6 use () instead of return
day: Math.floor(sec / 3600 / 24),
hour: Math.floor(sec / 3600 % 24),
minute: Math.floor(sec / 60 % 60),
second: Math.floor(sec % 60)
})),
tap(val => console.log(val)),
map(date => `${date.hour}: ${date.minute}: ${date.second}`)
);
}
private diffInSec = (start: Date, future: Date): number => {
const diff = future.getTime() - start.getTime();
return Math.floor(diff / this._MS_PER_SECOND);
}
}