前言

網站有了一堆的元素、動畫、圖片、文字,可能看起來很炫很厲害,但是這樣就只能是靜態的網站,想要實現購物車、會員機制、留言板、討論區、投票區、訊息發佈、商品發佈等等功能,進而設計出可與網友進行互動的功能。而要實現這些功能,不外乎就是一直呼叫 API,然後透過後端和資料庫的處理,再將資料傳回給網站。
前端和後端的連結,就是透過 Http 方法,包含 get、post、head、put、patch 和 delete。接下來就來說明如何在 Angular 2 中實現 HTTP 方法。

RxJS

RxJS是一個第三方函式庫,由 Angular 贊助,實現異步觀察模式。Angular 默認就有安裝 RxJS,因為 Observable 模式被廣泛在應用在 Angular 程序。Observable 是一種趨勢。HTTP 客戶端就需要 RxJS,所以我們必須採用額外的步驟開啟 RxJS Observables。
事實上可以只載入需要的部分,不過先全引入吧!
import rxjs-operator into app.component.ts

// Add all operators to Observable
import 'rxjs/Rx';

HTTP 服務

Angular Http client (客戶端) 和伺服器通訊是藉由熟習的 Http 要求/回應 (request/response) 協議。而且 Http client 應該是 Angular 中關於 Http 最常用到的函式庫了。

使用 Http client 的時候,應該將將服務註冊為依賴注入系統中。

直接看範例

GET

import { Injectable }     from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable }     from 'rxjs/Observable';
@Injectable()
export class HttpClientService {
  constructor (private http: Http) {}
  get(url: string, optionHeader?: {[header: string]: string}): Observable<Response> {
    let headers = new Headers();
    // set option header
    for(let header in optionHeader) {
      let value = optionHeader[header];
      headers.append(header, value);
    }
    return this.http.get(url, {headers: headers,})
                    .map(this.extractData)
                    .catch(this.handleError);
  }
  private extractData(res: Response) {
    let body = res.json();
    return body.data || { };
  }
  private handleError (error: Response | any) {
    // In a real world app, we might use a remote logging infrastructure
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}

首先先注入 http 進入 HttpClientService

constructor (private http: Http) {}

url 就是要 call 的 API 位置。
http.get 方法返回的是一個 HTTP 響應 Observable 對象,由 RxJS 庫提供,map()也是 RxJS 的一個操作符。

處理回應

map()函數中,我們會將提取數據映射到 this.extractData 函數

private extractData(res: Response) {
  if (res.status < 200 || res.status >= 300) {
    throw new Error('Bad response status: ' + res.status);
  }
  let body = res.json(); 
  return body.data || { };
}

Response 不能直接用,要先除錯,並將資料轉換成 json 給程式使用。

使用資料

getData() {
  this.HttpClientService.get(url:string)
                        .subscribe(
                          data => this.data = data,
                          error =>  this.errorMessage = <any>error
                        );
}

POST

方法也大同小異

post(url: string, body: any, optionHeader?: {[header: string]: string}): Observable<Response> {
    let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded'});
    // set option header
    for(let header in optionHeader) {
      let value = optionHeader[header];
      headers.append(header, value);
    }
    let newUrl = this.globalVariablesService.apiURL + url;
    return this.http.post(newUrl, body, {
      headers: headers,
    }).map(this.extractData)
      .catch(this.handleError);
}

另一種寫法

事實上我們也可以這樣寫

export class HttpClientService {
  ...
  get(url: string, optionHeader?: {[header: string]: string}): Observable<Response> {
    let headers = new Headers();
    // set option header
    for(let header in optionHeader) {
      let value = optionHeader[header];
      headers.append(header, value);
    }
    return this.http.get(url, {
      headers: headers,
    }).catch((res: Response) => this.handleError(res));
  }
  ...
}

當其他函數要調用 get 方法時注入,且先不處理成功的資料。

getData(): void {
  let url = 'htts://...'
  this.httpClientService.get(url)
      .subscribe(
        (res : Response) => {
          let data = res.json();
        },
        (err: any) => {
          console.log('err:',err);
        }
    );
}

前一種寫法適合寫明確回傳東西的服務,後者適合寫通用型,可以接受任何資料,最後在轉換。不管哪一種寫法都是將 http 方法和要最後處理的工作做區隔,發生錯誤時可以區分是 http 出錯還是 getData()出錯,取到資料後可以再拿來做其他事情。簡單來說就是將程式做層級化,邏輯根管理都比較容易。

AJAX

當然除了 Angular 官方提供的方法,AJAX 還是一樣受大家愛戴。
沒什麼技術好說的,一樣是異步,而且記得要引入 jQuery。比較麻煩的是 TSlint 看不懂 $,所以可以很作弊的這樣做。declare var $:any;放在 ts 最前面。

$.ajax({
  url: url,
  type: 'POST',
  data:  {'data':JSON.stringify(json)},
  success: function(data: any) {
      console.log(data);
  },
  error: function(XMLHttpRequest: any, textStatus: any, errorThrown: any) {
      console.log(errorThrown);
  }
});

AJAX 就不多說甚麼了,總之也是個方法。