Angular TIPS

Angular TIPS

サーバーサイドと非同期通信するには?(HttpClientサービス)

2018年3月19日

HttpClientサービスを使ってWeb APIと非同期通信するための基本的な方法を説明する。

  • このエントリーをはてなブックマークに追加

【対応バージョン】 Angular 5以降。v5時点で執筆しました。

 近年のフロントエンド開発において、非同期通信を受け持つXMLHttpRequestXHR)オブジェクトは欠かせません。JavaScript単体でできることは限られています。JavaScriptアプリでは、ページ上の操作に応じてXHRでサーバーに処理を要求し、その処理結果をページに反映させるのが基本です*1

  • *1 このようなアプリのことを、単一のページで全ての処理を完結させることから、SPASingle Page Application)と呼びます。

 HttpClientサービス*1は、このXHRオブジェクトのラッパーです。HttpClientサービスを利用することで、XHRオブジェクトでは冗長になりがちだった通信の手続きを、よりシンプルに記述できるようになります。

  • *1 以前は@angular/httpモジュールに属するHttpサービスがありましたが、Angular 5では非推奨の扱いとなっています。今後はHttpClientサービスを優先して利用してください。

 HttpClientサービスは、実にさまざまな機能(パラメーター)を持っていますが、本稿ではまず、HttpClientサービスを利用した基本的な非同期通信の手続きについて解説します。

HttpClientサービスの基本

 さっそく、具体的な例を見ていきましょう。以下は、テキストボックスに入力された名前に応じて、サーバーから「Hello, ●○!」のようなメッセージを受け取り、表示するサンプルです。

入力された名前をもとに、挨拶メッセージを表示する
入力された名前をもとに、挨拶メッセージを表示する
(1)HttpClientクラスをインポートする

 HttpClientサービスを利用するために、まずは、ルートモジュールからHttpClientModuleモジュールを有効にしておきます。これには、app.module.tsファイル(メインモジュール)で、@NgModuleデコレーターのimportsパラメーターにHttpClientModuleを追加してください。

TypeScript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule }   from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule, FormsModule, HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
HttpClientModuleモジュールをインポートするコード(app.module.ts)
(2)コンポーネントからHttpClientサービスを呼び出す

 HttpClientサービスを有効化できたところで、コンポーネントから実際に呼び出してみましょう*1

  • *1 本稿では、簡単化のために、コンポーネントからHttpClientサービスを直接呼び出していますが、望ましいコードではありません。実際のアプリでは、HTTP通信のコードはサービスとして切り出すようにしてください。
TypeScript
import { Component } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';

@Component({
  selector: 'app-root',
  template: `
    <form>
      <label for="name">名前:</label>
      <input id="name" name="name" type="text" [(ngModel)]="name" />
      <input type="button" (click)="onclick()" value="送信" />
    </form>
    <div>{{result}}</div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  name = '';    // 入力された名前
  result = '';  // 最終的に生成されるメッセージ

  // 1HttpClientサービスを注入
  constructor(private http: HttpClient) { }

  onclick() {
    // 2リクエストを発信
    this.http.get('app/http.php', {
      responseType: 'text',
      // 3URLにクエリパラメーターを指定
      params: new HttpParams().set('name', this.name),
    })
    .subscribe(
      data => {
        this.result = data
      },
      error => {
        this.result = '通信に失敗しました。';
      }
    );    
  }
}
HttpClientサービスでサーバーサイドに問い合わせるコード(app.component.ts)

 コンストラクター経由でHttpClientサービスを注入できる点は、他のサービスを利用する場合と同じです(詳しくは、別稿「TIPS:自作のサービスを定義するには?」も参照してください)。コンポーネントのhttpプロパティにインスタンスをセットしておきます(1)。

 実際にリクエストを発信しているのは、2です。getメソッドはHTTP GETメソッドでサーバーにリクエストを送信し、subscribeメソッドで応答データを処理します。

[構文]HttpClientクラス(getメソッド)

client
  .get(url [,options])
  .subscribe(
    data => { success_callback },
    error => { error_callback }   )

  • client: HttpClientサービス
  • url: リクエスト送信先のアドレス
  • options: リクエストオプション
    • success_callback: 通信に成功した場合の処理
    • error_callback: 通信に失敗した場合の処理

 引数optionsには「オプション名: 値」のハッシュとして、リクエストオプションを指定します。利用できるオプションについては、本連載でおいおい紹介していくとして、本稿で利用しているオプションのみ、以下でまとめておきます。

オプション 概要
responsType 応答の型(blobjsontextなど。デフォルトはjson
params クエリ情報
本稿で利用しているリクエストオプション

 paramsオプションには、HttpParamsオブジェクトを渡します。3ではnameキー(値はthis.name)だけを設定していますので、「~http.php?name=入力値」のようなURLが生成されます。値にマルチバイト文字が含まれている場合にも、内部的にエンコード処理されるので、アプリ側では意識しなくて構いません。

 subscribeメソッドの第1引数は、通信が成功した場合に実行されるコールバック関数を表します。引数として、レスポンス本体を受け取るので、本稿では、これをそのままresultプロパティに代入し、ページに反映しています。

 第2引数は、通信が失敗したときに実行されるコールバックです。引数としてエラー情報(HttpErrorResponseオブジェクト)を受け取ります。ここでは固定のメッセージを表示しているだけですが、HttpErrorResponseオブジェクトを介してステータス値(status)やエラーメッセージ(message)を取得することも可能です。

(3)サーバーサイドのコードを準備する

 HttpClientサービスからのリクエストを受け取るサーバーサイドのコードは、以下の通りです。本稿の守備範囲を超えるので、詳しい説明は割愛します。コード内コメントからおおよその意味を把握してください。

PHP
<?php
// クエリ情報nameを取得
$r = $_GET['name'];
// クエリ情報nameが空の場合はエラー
if (empty($r)) {
  header('HTTP/1.1 500 Internal Server Error');
} else {
  // 空でなければ、メッセージを出力
  print('Hello,'.$r.'!');
}
クライアントからの要求を受けてメッセージを応答するコード(http.php)
(4)Apacheなどのサーバーに配置する

 本稿のサンプルは、サーバーサイドでPHPを利用しているため、Angular CLI標準の開発サーバーでは動作しません。以下のようにアプリをビルドします。

ターミナル
> ng build
Date: 2018-02-06T08:02:46.345Z
Hash: 632fd435be0740ca688b
Time: 12651ms
……中略……
chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.85 MB [initial] [rendered]
アプリをビルドするコマンド

 /distフォルダーにビルド結果が出力されるので、その配下にapp/http.phpをコピーします。

(5)基底パスを変更する

 /distフォルダー配下のindex.htmlを開いて、<head>要素の中にある<base>要素を書き換えます。

HTML
<base href="/dist/" />
基底パスを変更(index.htmlから抜粋)

 以上で準備は完了です。あとは、/distフォルダーをApacheの/htdocsフォルダーに丸ごと配置すれば、「http://localhost/dist/」でアプリにアクセスできます。

処理対象:サービス(Service) カテゴリ:基本
処理対象:クラス(CLASS) カテゴリ:サービス(Service)
API:HttpClient カテゴリ:@angular > common > CLASS(クラス)

※以下では、本稿の前後を合わせて5回分(第44回~第48回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

Angular TIPS
44. 式の値によってメッセージを切り替えるには?(ngSwitch)

JavaScriptのswitch文のように、ある式を評価して、その式の値に応じて、表示するメッセージを切り替える方法を説明する。

Angular TIPS
45. 自作のサービスを定義するには?(@Injectableデコレーター)

既存のコンポーネントからアプリ固有のロジックをサービスとして切り出すための基本的な方法を説明する。

Angular TIPS
46. 【現在、表示中】≫ サーバーサイドと非同期通信するには?(HttpClientサービス)

HttpClientサービスを使ってWeb APIと非同期通信するための基本的な方法を説明する。

Angular TIPS
47. ファイルをサーバーにアップロードするには?(HttpClientサービス)

HttpClientサービスを使ってサーバーにファイルをアップロードするための基本的な方法を説明する。

Angular TIPS
48. コンポーネントからページのメタ情報を操作するには?(Meta)

Metaサービスを使って、コンポーネントからページのメタ情報を操作する方法を説明する。

サイトからのお知らせ

Twitterでつぶやこう!