Project - Groupbuy App Part IV

prev

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);
  }
}

   Reprint policy


《Project - Groupbuy App Part IV》 by Tong Shi is licensed under a Creative Commons Attribution 4.0 International License
  TOC