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. (
HttpClientModule
from@angular/common/http
) - Inject it in the constructor.
constructor(private http: HttpClient) {}
- Use its
get/post/put/delete
methods. 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
root
module.
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);
}
}