Angular2でAjaxを使ってデータを取得する(Httpクライアント)

Angular2でAjaxを使ってデータを取得する(Httpクライアント)

画面遷移のないシングルページのWebアプリケーションでは、Ajaxが使えないと話にならないですよね。
Angular2ではHttpクライアントが機能として含まれています。

今回はAngular2でのHttpクライアントでデータを取得する方法を紹介します。

Httpクライアント

これから全部で3つのコードを紹介します。

今回例で使うソースコードは公式リファレンスで紹介されているものです。
Ajaxで取得したデータをリストで表示します。

app/toh.component.ts

import { Component }         from '@angular/core';
import { HTTP_PROVIDERS }    from '@angular/http';
import { HeroListComponent } from './hero-list.component';
import { HeroService }       from './hero.service';

@Component({
    selector: 'my-toh',
    template: `
        <h1>Tour of Heroes</h1>
        <hero-list></hero-list>
    `,
    directives: [HeroListComponent],
    providers:  [
        HTTP_PROVIDERS,
        HeroService,
    ]
})
export class TohComponent { }

ではまずはじめに、ベースとなるリスト表示のコンポーネントを定義します。

なんかいっぱい書いてあって、よくわからないですよねw
大丈夫です、ひとつずつ見ていきましょう。

まず、 HTTP_PROVIDERS をimportします。これは、Httpクライアントを使うための処理が定義されています。
@Componentproviders プロパティに指定しておきます。

実際にHttpクライアントで通信する機能は、HeroService に任せます。これも providers に追加します。
これは、Angular2のDependency Injection(依存的注入) という機能で外部化しています。
(Dependency Injectionについては後日詳しく紹介します。ここでは、処理を使いまわせる仕組みと考えていれば大丈夫です。)

Angular2ではコンポーネント志向の設計なので、処理を独立させることが簡単にできます。
コードを見ると、すっきりしていてストレスフリーですね。

app/toh/hero-list.component.ts (class)

次に、先ほど定義した TohComponent のリスト部分を定義します。
TohComponent内の <hero-list></hero-list> の部分がこれにあたります。

import { Component, OnInit } from '@angular/core';
import { Hero}               from './hero';
import { HeroService }       from './hero.service';

@Component({
    selector: 'hero-list',
    template: `
        <h1>Tour of Heroes ()</h1>
        <h3>Heroes:</h3>
        <ul>
            <li *ngFor="let hero of heroes">
                {{hero.name}}
            </li>
        </ul>
        New hero name:
        <input #newHeroName />
        <button (click)="addHero(newHeroName.value); newHeroName.value=''">
            Add Hero
        </button>
        <div class="error" *ngIf="errorMessage">{{errorMessage}}</div>
    `
    ...
})
export class HeroListComponent implements OnInit {

    constructor (private heroService: HeroService) {}

    errorMessage: string;
    heroes: Hero[];

    ngOnInit() { this.getHeroes(); }

    getHeroes() {
        this.heroService.getHeroes()
            .subscribe(
                heroes => this.heroes = heroes,
                error =>  this.errorMessage = <any>error
            );
    }

}

初期化のタイミングで、getHeroes メソッドで heroService.getHeroes() を実行して、Http通信をしています。
ngOnInit についてはAngular2のLifecycle Hooksを理解するを参考にしてください。

getHeroes メソッド内の subscribe() というのはRxJSの機能です。今はHttpの処理が終わった後に、
実行されるものだと思っていれば問題ありません。

Httpでデータを取得した後は、HeroListComponent自身の heroes メンバーに保存しています。
データが保存されれば、<li *ngFor="let hero of heroes"></li>heroes に自動的に反映されて、リストが生成されます。

app/toh/hero.service.ts

最後にHttpサービス本体です。

import { Injectable }     from '@angular/core';
import { Http, Response } from '@angular/http';
import { Hero }           from './hero';
import { Observable }     from 'rxjs/Observable';

@Injectable()
export class HeroService {

    constructor (private http: Http) {}

    private heroesUrl = 'app/heroes';  // URL to web api

    getHeroes (): Observable<Hero[]> {
        return this.http.get(this.heroesUrl)
            .map(this.extractData)
            .catch(this.handleError);
    }

    // レスポンスデータの整形処理
    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 || { };
    }

    // エラー処理
    private handleError (error: any) {
        // In a real world app, we might send the error to remote logging infrastructure
        let errMsg = error.message || 'Server error';
        console.error(errMsg); // log to console instead
        return Observable.throw(errMsg);
    }
}

はじめに、@Injectable で他のコンポーネントからimportできるように宣言しています。

Httpを使うためには、import { Http } でHttpオブジェクトをimportする必要があります。
そして、constructorの引数で、Httpオブジェクトをクラスのhttpメンバーに追加します。
※TypeScriptでは、constructorの引数でprivatepublic などのキーワードをつけると、そのクラスのメンバーに追加されます。

ここでは、getHeroes メソッドに注目しましょう。
http.get()get メソッドでの通信を行います。第1引数にはURLを指定します。
返り値はRxJSのObservableというデータになりますが、今は理解できなくても大丈夫です。
ObservableのmapメソッドでAjaxでのレスポンスをチェック、json化しています。
RxJSは理解が難しいので、ここではレスポンスデータを整形していると認識できればOKです。
catch() ではエラーハンドリングをしています。

レスポンスデータをコンポーネントで受け取る

HeroListComponentgetHeroes メソッドがあったのを覚えていますか?

    getHeroes() {
        this.heroService.getHeroes()
            .subscribe(
                heroes => this.heroes = heroes,
                error =>  this.errorMessage = <any>error
            );
    }

この中の subscribe メソッドの第1引数に渡す関数の内で、レスポンスデータにアクセス出来ます。
データがコンポーネントに保存されると、自動的にリスト表示されるという仕組みです。

複雑だけど、まずは使って慣れよう

ファイルが複数に分かれていたり、
いろんな技術が一気に出てきてかなり複雑ですが、
要点を抑えて慣れてしまえばこっちのもの。
Httpクライアントはとても頻繁に使うので、ぜひとも理解してもらいたいです。

今回は get メソッドの紹介でしたが、今後 post メソッドなども紹介します。

参考

五十川 洋平(Yohei Isokawa)

五十川 洋平(Yohei Isokawa)

フロントエンドエンジニア/面白法人カヤックなどのWeb制作会社に勤務したのち、故郷の新潟に戻り独立。JSフレームワークAngularやFirebase、Google Cloud Platformを使ったWebアプリ開発が得意。 また、Udemyのプログラミング解説の講師、writer.appの自主開発や上越TechMeetupの主催などを行っています。

プロフィール

©Copyright 2022 Yohei Isokawa All Rights Reserved.