概要
Angular 4.3からこれまでのHttpModuleに代わってより軽量かつ使いやすいHttpClientModuleと言うものが出てきました。
今回はその移行作業を書きます。
環境
- Angular 4.3.1
- angular-cli 1.2.3
成果物
今回の成果物は以下です。 github.com
比較して見やすいのは以下のdiffです。 github.com
使い方のBefore / After
app.module.ts
Before
@angular/http
のHttpModule
を使います。
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { ArticleService } from './services'; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, HttpModule ], providers: [ArticleService], bootstrap: [AppComponent] })
After
HttpModule
の代わりに@angular/common/http
のHttpClientModule
を使います。
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; // here import { AppComponent } from './app.component'; import { ArticleService } from './services'; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, HttpClientModule // here ], providers: [ArticleService], bootstrap: [AppComponent] }) export class AppModule { }
コード
Before
Http
を使います。
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import { Article } from './article'; @Injectable() export class ArticleService { constructor(private http: Http) { } get(id: string): Observable<Article> { return this.http.get('http://jsonplaceholder.typicode.com/posts/' + id) .map(res => res.json()) } }
After
Http
の代わりにHttpClient
を使います。
また.get<Article>()
のようにジェネリクスを使って書くことで、.json()
でわざわざ変換していた処理が省略できるようになりました。
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { Article } from './article'; @Injectable() export class ArticleService { constructor(private http: HttpClient) { } get(id: string): Observable<Article> { return this.http.get<Article>('http://jsonplaceholder.typicode.com/posts/' + id); } }
テスト
Before
MockBackend
やらBaseRequestOptions
でゴニョゴニョとproviderを用意します。
import { TestBed, inject } from '@angular/core/testing'; import { Http, BaseRequestOptions, Response, ResponseOptions, Request, RequestMethod } from '@angular/http'; import { MockBackend } from '@angular/http/testing'; import { ArticleService } from './article.service'; import { Article } from './article'; describe('ArticleService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ ArticleService, { provide: Http, useFactory: (mockBackend, options) => { return new Http(mockBackend, options); }, deps: [MockBackend, BaseRequestOptions] }, MockBackend, BaseRequestOptions ] }); }); describe('#get', () => { const mockResponse: Article = { id: 0, userId: 1, title: 'mock title', body: 'mock body', } it('should get article', inject([ArticleService, MockBackend], (service, mockBackend) => { let req: Request; mockBackend.connections.subscribe((connection) => { connection.mockRespond(new Response(new ResponseOptions({ body: JSON.stringify(mockResponse) }))); req = connection.request; }); service.get('0').subscribe((resp: Article) => { expect(req.url).toBe('http://jsonplaceholder.typicode.com/posts/0'); expect(req.method).toBe(RequestMethod.Get); expect(resp.id).toBe(0); expect(resp.userId).toBe(1); expect(resp.title).toBe('mock title'); expect(resp.body).toBe('mock body'); }); })); }); });
After
テストは書き方がかなり変わります。providers周りなど、以前よりとてもシンプルに書けるようになります。
ポイントをコメントで追記しています。
import { TestBed, inject } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ArticleService } from './article.service'; import { Article } from './article'; describe('ArticleService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ ArticleService, ], imports: [ HttpClientTestingModule, ] }); }); describe('#get', () => { const mockResponse: Article = { id: 0, userId: 1, title: 'mock title', body: 'mock body', }; it('should get article', inject([ArticleService, HttpTestingController], (service, httpMock) => { // リクエストの用意。 service.get('0').subscribe((resp: Article) => { expect(resp.id).toBe(0); expect(resp.userId).toBe(1); expect(resp.title).toBe('mock title'); expect(resp.body).toBe('mock body'); }); // 指定したエンドポイントのリクエストをここでpending。 const req = httpMock.expectOne('http://jsonplaceholder.typicode.com/posts/0'); expect(req.request.method).toEqual('GET'); // ここでレスポンスを返す req.flush(mockResponse); // 余分なリクエストがないかチェック httpMock.verify(); })); }); });
まとめ
基本的な移行作業を書きました。
他にもHeadersがHttpHeadersになったり、Response型の代わりにHttpResponseになったりと、ちょこちょこ変更点があります。
それぞれドキュメントのメソッドや例を見て修正すると良いです。