このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

web帳

記事詳細

2017.04.09

Angular2の始め方。Angular2 公式チュートリアル – HTTP(簡単な和訳)

HTTP

GETTING AND SAVING DATA

こちらのページは、以下のページの簡単な和訳です。

Angular2 tutorial | HTTP

前回
Angular2の始め方。Angular2 公式チュートリアル – Routing(簡単な和訳)

その他

Angular2の始め方。Angular2 公式チュートリアル – Services(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – Multiple Components(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – Master/Detail(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – SETUP FOR LOCAL DEVELOPMENT(簡単な和訳)Angular2の始め方。Angular2 公式チュートリアル – Introduction(簡単な和訳)

Angular2の始め方。Angular2 公式チュートリアル – The Hero Editor(簡単な和訳)

AngularのHTTPサービスを使用するようにサービスとコンポーネントを変換します。

このページでは、次の点を改善します。

・ヒーローデータをサーバーから取得します。

・主人公の名前を追加、編集、削除できるようにします。

・変更をサーバーに保存します。

あなたは、リモートサーバーのWeb APIに対応するHTTP呼び出しを行うようにアプリを教えます。

このページが終了したら、アプリはこの live example / downloadable example.のようになります。

Where you left off

前のページでは、ダッシュボードと固定ヒーローリストの間をナビゲートし、選択されたヒーローを途中で編集する方法を学びました。それがこのページの出発点です。

Keep the app transpiling and running

次のコマンドを入力します。

npm start

このコマンドは、”watch mode”で、TypeScriptコンパイラを実行し、コードが変更されたときに自動的に再コンパイルします。

このコマンドは、ブラウザでアプリを同時に起動し、コードが変更されたときにブラウザを更新します。

ブラウザを再コンパイルまたは更新するために一時停止することなく、ヒーローのツアーを構築し続けることができます。

Providing HTTP Services

HttpModuleはCore Angularモジュールではありません。

HttpModuleは、Webアクセスに対するAngularのオプションのアプローチです。

これは、@angular/http と呼ばれる個別のアドオンモジュールとして存在し、Angular npmパッケージの一部として別のスクリプトファイルに同梱されています。

systemjs.configは必要なときにそのライブラリをロードするように設定しているので、 @angular/httpからインポートする準備は整っています。

Register for HTTP services

このアプリケーションはAngular httpサービスに依存します。

これは他のサポートサービスに依存します。

@angular/httpライブラリのHttpModuleは、プロバイダに完全なHTTPサービスのセットを保持します。

アプリケーションのどこからでもこれらのサービスにアクセスできるようにするには、HttpModuleをAppModuleのインポートリストに追加します。

src/app/app.module.ts (v1)

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { HttpModule }    from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService }          from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent,
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

ルートNgModule AppModuleのインポート配列の一部としてHttpModuleも提供することに注意してください。

Simulate the web API

アプリケーション全体のサービスをルートAppModuleプロバイダに登録することをお勧めします。

ヒーローデータのリクエストを処理できるWebサーバーがなければ、HTTPクライアントはモックサービス(メモリ内Web API)からデータをフェッチして保存します。

src/app/app.module.tsをモックサービスを使用するこのバージョンで更新してください:

src/app/app.module.ts (v2)

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

import { AppRoutingModule } from './app-routing.module';

// Imports for loading & configuring the in-memory web api
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService }  from './in-memory-data.service';

import { AppComponent }         from './app.component';
import { DashboardComponent }   from './dashboard.component';
import { HeroesComponent }      from './heroes.component';
import { HeroDetailComponent }  from './hero-detail.component';
import { HeroService }          from './hero.service';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    InMemoryWebApiModule.forRoot(InMemoryDataService),
    AppRoutingModule
  ],
  declarations: [
    AppComponent,
    DashboardComponent,
    HeroDetailComponent,
    HeroesComponent,
  ],
  providers: [ HeroService ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

実際のAPIサーバーを必要とするのではなく、InMemoryWebApiModuleをモジュールのインポートに追加することでリモートサーバーとの通信をシミュレートし、HttpクライアントのXHRバックエンドサービスをメモリ内の代替方法で効果的に置き換えます。

InMemoryWebApiModule.forRoot(InMemoryDataService),

forRoot()設定メソッドは、メモリ内のデータベースを準備するInMemoryDataServiceクラスを取ります。

app内のファイルin-memory-data.service.tsを次の内容で追加します。

src/app/in-memory-data.service.ts

import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    let heroes = [
      {id: 11, name: 'Mr. Nice'},
      {id: 12, name: 'Narco'},
      {id: 13, name: 'Bombasto'},
      {id: 14, name: 'Celeritas'},
      {id: 15, name: 'Magneta'},
      {id: 16, name: 'RubberMan'},
      {id: 17, name: 'Dynama'},
      {id: 18, name: 'Dr IQ'},
      {id: 19, name: 'Magma'},
      {id: 20, name: 'Tornado'}
    ];
    return {heroes};
  }
}

このファイルはmock-heroes.tsに置き換えられました。これは今でも安全に削除できます。

“““
メモリ内のWeb APIは、開発の初期段階やこの「英雄ツアー」などのデモンストレーションにのみ役立ちます。

このバックエンド置換の詳細については心配しないでください。

あなたは本当のWeb APIサーバーを持っているときにスキップすることができます。

メモリ内Web APIの詳細については、「HTTP Client」ページの付録「 Appendix: Tour of Heroes in-memory web api」を参照してください。
“““

Heroes and HTTP

現在のHeroService実装では、模擬ヒーローで解決されたPromiseが返されます。

getHeroes(): Promise<Hero[]> {
  return Promise.resolve(HEROES);
}

彼は非同期操作でなければならないHTTPクライアントでヒーローを最終的に取得することを期待して実装されました。

HTTPを使用するようにgetHeroes()を変換します。

src/app/hero.service.ts (updated getHeroes and new class members)

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

  constructor(private http: Http) { }

  getHeroes(): Promise<Hero[]> {
    return this.http.get(this.heroesUrl)
               .toPromise()
               .then(response => response.json().data as Hero[])
               .catch(this.handleError);
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error); // for demo purposes only
    return Promise.reject(error.message || error);
  }

次のようにインポート文を更新します。

src/app/hero.service.ts (updated imports)

import { Injectable }    from '@angular/core';
import { Headers, Http } from '@angular/http';

import 'rxjs/add/operator/toPromise';

import { Hero } from './hero';

ブラウザを更新します。主人公のデータがモックサーバーから正常に読み込まれるはずです。

HTTP Promise

Angular http.getは、RxJS Observableを返します。

Observablesは、非同期データフローを管理する強力な方法です。

このページの後半でObservablesについて読むことにします。

今のところ、toPromise演算子を使用してObservableをPromiseに変換しました。

.toPromise()

Angular Observableには、toPromise演算子は用意されていません。

便利な機能で、Observableを拡張するtoPromiseのような多くの演算子があります。

これらの機能を使用するには、演算子自体を追加する必要があります。

これはRxJSライブラリから次のようにインポートするのと同じくらい簡単です:

import 'rxjs/add/operator/toPromise';

“““
このチュートリアルの後半では、演算子を追加して、それを行う必要がある理由を学習します。
“““

Extracting the data in the then callback

Promiseのthen()コールバックでは、HTTP応答のjsonメソッドを呼び出して、応答内のデータを抽出します。

.then(response => response.json().data as Hero[])

レスポンスJSONには、呼び出し元が望むヒーローの配列を保持する単一のdataプロパティがあります。

したがって、配列を取得し、それを解決されたPromise値として返します。

“““
サーバーが返すデータの形に注意してください。

この特定のメモリ内Web APIの例は、dataプロパティを持つオブジェクトを返します。

APIが何かを返すかもしれません。 Web APIに合わせてコードを調整します。
“““

呼び出し元はあなたが(モック)サーバーからヒーローを取り込んだことに気付かない。

それは前と同じように、heroesのPromiseを受けます。

Error Handling

getHeroes()の終了時に、サーバーの障害を捕捉してエラーハンドラに渡します。

.catch(this.handleError);

これは重要なステップです。 HTTP障害は、制御できない理由で頻繁に発生するため、予期しなければなりません。

private handleError(error: any): Promise<any> {
  console.error('An error occurred', error); // for demo purposes only
  return Promise.reject(error.message || error);
}

このデモサービスでは、エラーをコンソールに記録します。実際には、コードのエラーを処理します。デモのために、これは動作します。

また、コードには、拒否された約束事で発呼者にエラーが含まれているため、発呼者は適切なエラーメッセージをユーザに表示することができる。

Get hero by id

HeroDetailComponentがHeroServiceにヒーローをフェッチするように要求すると、HeroServiceは現在、一致するIDを持つヒーローとフィルタをすべてフェッチします。

それはシミュレーションでは問題ありませんが、実際のサーバーにすべてのヒーローを求めるのは無駄です。

ほとんどのWeb APIは、api/hero/:idの形式(api/hero/11など)でget-by-idリクエストをサポートしています。

HeroService.getHeroメソッドを更新して、get-by-idリクエストを作成します。

src/app/hero.service.ts (getHero)

getHero(id: number): Promise<Hero> {
  const url = `${this.heroesUrl}/${id}`;
  return this.http.get(url)
    .toPromise()
    .then(response => response.json().data as Hero)
    .catch(this.handleError);
}

このリクエストはgetHeroesとほとんど同じです。 URLのヒーローIDは、サーバーが更新するヒーローを識別します。

また、応答のデータは配列ではなく単一のヒーローオブジェクトです。

Unchanged getHeroes API

getHeroes()とgetHero()に対して内部的な変更を行ったにもかかわらず、パブリックシグネチャは変更されませんでした。

両方の方法からPromiseを返します。それらを呼び出すコンポーネントを更新する必要はありません。

今度はヒーローの作成と削除の機能を追加しましょう。

Updating hero details

ヒーローの詳細ビューでヒーローの名前を編集してみてください。

入力すると、ヒロの名前がビューの見出しで更新されます。しかし、[戻る]ボタンをクリックすると、変更が失われます。

更新は以前は失われていませんでした。何が変わったの?アプリが模擬ヒーローのリストを使用したとき、アプリ全体の共有リスト内のヒーローオブジェクトに更新が直接適用されました。

サーバーからデータを取得するようになったので、変更を維持するには、サーバーに書き戻す必要があります。

Add the ability to save hero details

ヒーロー詳細テンプレートの最後に、saveという名前の新しいコンポーネントメソッドを呼び出すclickイベントバインディングを含む保存ボタンを追加します。

src/app/hero-detail.component.html (save)

<button (click)="save()">Save</button>

ヒーローサービスの更新メソッドを使用してヒーロー名の変更を保持してから、前のビューに戻る、次の保存メソッドを追加します。

src/app/hero-detail.component.ts (save)

save(): void {
  this.heroService.update(this.hero)
    .then(() => this.goBack());
}
Add a hero service update method

src/app/hero.service.ts (update)

private headers = new Headers({'Content-Type': 'application/json'});

update(hero: Hero): Promise<Hero> {
  const url = `${this.heroesUrl}/${hero.id}`;
  return this.http
    .put(url, JSON.stringify(hero), {headers: this.headers})
    .toPromise()
    .then(() => hero)
    .catch(this.handleError);
}

サーバーが更新するヒーローを特定するために、ヒーローIDがURLにエンコードされます。

put()本文は、JSON.stringifyを呼び出して取得したヒーローのJSON文字列エンコーディングです。

ボディコンテンツタイプ(application / json)は、リクエストヘッダで識別されます。

ブラウザを更新し、ヒーロー名を変更して変更を保存し、ブラウザの[戻る]ボタンをクリックします。変更は今も続くはずです。

Add the ability to add heroes

主人公を追加するには、主人公の名前が必要です。

追加ボタンとペアになった入力要素を使用することができます。

ヒーローズコンポーネントのHTMLに、見出しの直後に以下を挿入します。

src/app/heroes.component.html (add)

<div>
  <label>Hero name:</label> <input #heroName />
  <button (click)="add(heroName.value); heroName.value=''">
    Add
  </button>
</div>

クリックイベントに応じて、コンポーネントのクリックハンドラを呼び出し、入力フィールドをクリアして別の名前の準備が整うようにします。

src/app/heroes.component.ts (add)

add(name: string): void {
  name = name.trim();
  if (!name) { return; }
  this.heroService.create(name)
    .then(hero => {
      this.heroes.push(hero);
      this.selectedHero = null;
    });
}

指定された名前が空でない場合、ハンドラは指定されたヒーローの作成をヒーローサービスに委任し、次に新しいヒーローを配列に追加します。

HeroServiceクラスにcreateメソッドを実装します。

src/app/hero.service.ts (create)

create(name: string): Promise<Hero> {
  return this.http
    .post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
    .toPromise()
    .then(res => res.json().data as Hero)
    .catch(this.handleError);
}

ブラウザをリフレッシュしていくつかのヒーローを作成します。

Add the ability to delete a hero

主人公のビューの各ヒーローには削除ボタンが必要です。

heroesコンポーネントのHTMLに、繰り返した <li>タグのヒーロー名の後に次のボタン要素を追加します。

<button class="delete"
  (click)="delete(hero); $event.stopPropagation()">x</button>

<li>要素は次のようになります。

src/app/heroes.component.html (li-element)

<li *ngFor="let hero of heroes" (click)="onSelect(hero)" [class.selected]="hero === selectedHero">
  <span class="badge">{{hero.id}}</span>
  <span>{{hero.name}}</span>
  <button class="delete" (click)="delete(hero); $event.stopPropagation()">x</button>
</li>

コンポーネントのdelete()メソッドを呼び出すことに加えて、削除ボタンのクリックハンドラコードはクリックイベントの伝播を停止します。

そのため、

  • クリックハンドラがトリガされることは望ましくありません。削除します。

    delete()ハンドラのロジックはやや難解です:

    src/app/heroes.component.ts (delete)

    delete(hero: Hero): void {
      this.heroService
          .delete(hero.id)
          .then(() => {
            this.heroes = this.heroes.filter(h => h !== hero);
            if (this.selectedHero === hero) { this.selectedHero = null; }
          });
    }

    もちろん、主人公の削除を主人公のサービスに委任しますが、コンポーネントは表示を更新する責任があります。

    削除された主人公を削除し、必要に応じて選択したヒーローをリセットします。

    ヒーローエントリの右端に削除ボタンを配置するには、この追加のCSSを追加します:

    src/app/heroes.component.css (additions)

    button.delete {
      float:right;
      margin-top: 2px;
      margin-right: .8em;
      background-color: gray !important;
      color:white;
    }
    Hero service delete() method

    ヒーローサービスのdelete()メソッドを追加します。このメソッドはdelete()HTTPメソッドを使用して、ヒーローをサーバーから削除します。

    src/app/hero.service.ts (delete)

    delete(id: number): Promise<void> {
      const url = `${this.heroesUrl}/${id}`;
      return this.http.delete(url, {headers: this.headers})
        .toPromise()
        .then(() => null)
        .catch(this.handleError);
    }

    ブラウザを更新して、新しい削除機能を試してください。

    Observables

    各Httpサービスメソッドは、Observable of HTTP Responseオブジェクトを返します。

    HeroServiceはObservableをPromiseに変換し、呼び出し元に約束を返します。このセクションでは、Observableをどのように、いつ、なぜ、なぜ直接返すのかを示します。

    Background

    Observableは配列のような演算子で処理できるイベントのストリームです。

    Angular coreは、observablesを基本的にサポートしています。

    開発者は、RxJSライブラリの演算子と拡張機能を使用してそのサポートを強化します。あなたはすぐに表示されます。

    HeroServiceは、toPromise演算子をhttp.get()のObservable結果にチェーンすることを思い出してください。

    そのオペレータはObservableをPromiseに変換し、その約束を呼び出し元に返しました。

    プロミスへの変換は、しばしば良い選択です。

    通常、http.get()にデータの単一のチャンクをフェッチするように依頼します。データを受信すると、完了です。呼び出しコンポーネントは、Promiseの形式で単一の結果を簡単に消費することができます。

    しかし、要求は必ずしも一度しか行われません。サーバーが最初の要求に応答する前に、1つの要求を開始して取り消し、別の要求を行うことができます。

    request-cancel-new-requestシーケンスは、関数Promise(){[native code]}で実装するのは困難ですが、sでは簡単です。

    Add the ability to search by name

    src/app/hero-search.service.ts

    import { Injectable } from '@angular/core';
    import { Http }       from '@angular/http';
    import { Observable }     from 'rxjs/Observable';
    import 'rxjs/add/operator/map';
    import { Hero }           from './hero';
    @Injectable()
    export class HeroSearchService {
      constructor(private http: Http) {}
      search(term: string): Observable<Hero[]> {
        return this.http
                   .get(`app/heroes/?name=${term}`)
                   .map(response => response.json().data as Hero[]);
      }
    }

    HeroSearchServiceのhttp.get()呼び出しはHeroServiceのhttp.get()呼び出しと似ていますが、URLには現在クエリ文字列があります。

    もっと重要なことに、あなたはもはやtoPromise()を呼び出すことはありません。

    代わりに、Observableをhtttp.get()から別のRxJS演算子、map()にチェーンして返信して、レスポンスデータからヒーローを抽出します。

    RxJS演算子連鎖は、応答処理を簡単かつ読みやすくします。オペレータについての以下の説明を参照してください。

    HeroSearchComponent

    新しいHeroSearchServiceを呼び出すHeroSearchComponentを作成します。

    コンポーネントテンプレートはシンプルで、テキストボックスと一致する検索結果のリストです。

    src/app/hero-search.component.html

    <div id="search-component">
      <h4>Hero Search</h4>
      <input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
      <div>
        <div *ngFor="let hero of heroes | async"
             (click)="gotoDetail(hero)" class="search-result" >
          {{hero.name}}
        </div>
      </div>
    </div>

    また、新しいコンポーネントのスタイルを追加します。

    src/app/hero-search.component.css

    .search-result{
      border-bottom: 1px solid gray;
      border-left: 1px solid gray;
      border-right: 1px solid gray;
      width:195px;
      height: 16px;
      padding: 5px;
      background-color: white;
      cursor: pointer;
    }
    .search-result:hover {
      color: #eee;
      background-color: #607D8B;
    }
    #search-box{
      width: 200px;
      height: 20px;
    }

    ユーザーが検索ボックスに入力すると、keyupイベントバインディングによって、コンポーネントのsearch()メソッドが新しい検索ボックスの値で呼び出されます。

    予想どおり、*ngForは、コンポーネントのヒーロープロパティからヒーローオブジェクトを繰り返します。

    しかし、あなたがすぐに見るように、英雄のプロパティは、英雄の配列ではなく、英雄の配列のオブザーバブルです。

    *ngForは、非同期パイプ(AsyncPipe)を経由するまでObservableで何もできません。

    非同期パイプはObservableにサブスクライブし、*ngForのヒーローの配列を生成します。

    HeroSearchComponentクラスとメタデータを作成します。

    src/app/hero-search.component.ts

    import { Component, OnInit } from '@angular/core';
    import { Router }            from '@angular/router';
    import { Observable }        from 'rxjs/Observable';
    import { Subject }           from 'rxjs/Subject';
    // Observable class extensions
    import 'rxjs/add/observable/of';
    // Observable operators
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/operator/debounceTime';
    import 'rxjs/add/operator/distinctUntilChanged';
    import { HeroSearchService } from './hero-search.service';
    import { Hero } from './hero';
    @Component({
      selector: 'hero-search',
      templateUrl: './hero-search.component.html',
      styleUrls: [ './hero-search.component.css' ],
      providers: [HeroSearchService]
    })
    export class HeroSearchComponent implements OnInit {
      heroes: Observable<Hero[]>;
      private searchTerms = new Subject<string>();
      constructor(
        private heroSearchService: HeroSearchService,
        private router: Router) {}
      // Push a search term into the observable stream.
      search(term: string): void {
        this.searchTerms.next(term);
      }
      ngOnInit(): void {
        this.heroes = this.searchTerms
          .debounceTime(300)        // wait 300ms after each keystroke before considering the term
          .distinctUntilChanged()   // ignore if next search term is same as previous
          .switchMap(term => term   // switch to new observable each time the term changes
            // return the http search observable
            ? this.heroSearchService.search(term)
            // or the observable of empty heroes if there was no search term
            : Observable.of<Hero[]>([]))
          .catch(error => {
            // TODO: add real error handling
            console.log(error);
            return Observable.of<Hero[]>([]);
          });
      }
      gotoDetail(hero: Hero): void {
        let link = ['/detail', hero.id];
        this.router.navigate(link);
      }
    }
    SEARCH TERMS

    searchTermsに焦点を当てる:

    private searchTerms = new Subject<string>();
    
    // Push a search term into the observable stream.
    search(term: string): void {
      this.searchTerms.next(term);
    }

    サブジェクトは、観測可能なイベントストリームのプロデューサです。 searchTermsは、名前の検索のフィルタ条件であるObservable of stringsを生成します。

    search()を呼び出すたびに、next()を呼び出すことによって、この文字列の観測可能なストリームに新しい文字列が追加されます。

    INITIALIZE THE HEROES PROPERTY (NGONINIT)

    被験者はまた観察可能である。検索用語のストリームをHero配列のストリームに変換し、その結果をheroesプロパティに割り当てることができます。

    heroes: Observable<Hero[]>;
    
    ngOnInit(): void {
      this.heroes = this.searchTerms
        .debounceTime(300)        // wait 300ms after each keystroke before considering the term
        .distinctUntilChanged()   // ignore if next search term is same as previous
        .switchMap(term => term   // switch to new observable each time the term changes
          // return the http search observable
          ? this.heroSearchService.search(term)
          // or the observable of empty heroes if there was no search term
          : Observable.of<Hero[]>([]))
        .catch(error => {
          // TODO: add real error handling
          console.log(error);
          return Observable.of<Hero[]>([]);
        });
    }

    すべてのユーザーのキーストロークを直接HeroSearchServiceに渡すと、過度の量のHTTP要求が作成され、サーバーリソースに課金され、セルラーネットワークのデータプランを介して焼き付けられます。

    代わりに、Observable演算子を連鎖させて、Observableという文字列への要求の流れを減らすことができます。

    HeroSearchServiceへの呼び出し回数を減らしても、タイムリーな結果が得られます。方法は次のとおりです。

    debounceTime(300)は、最新の文字列を渡す前に、新しい文字列イベントのフローが300ミリ秒間停止するまで待機します。 300ミリ秒以上の頻度でリクエストを行うことはありません。

    ・ distinctUntilChangedは、フィルター・テキストが変更された場合にのみ要求が送信されるようにします。

    ・ switchMap()は、debounceおよびdistinctUntilChangedを使用して検索する各検索タームの検索サービスを呼び出します。

    ・ これは、以前の検索オブザーバブルをキャンセルして破棄し、観察可能な最新の検索サービスのみを返します。

    “““
    switchMap operator (以前はflatMapLatest)を使用して、すべての修飾キーイベントがhttp()メソッド呼び出しをトリガーできます。リクエスト間に300msの休止時間があっても、飛行中に複数のHTTPリクエストを持つことができ、送信された順序で返されないことがあります。

    switchMap()は、最新のhttpメソッド呼び出しからのオブザーバブルのみを返しながら、元のリクエストの順序を保持します。以前のコールの結果はキャンセルされ、破棄されます。

    検索テキストが空の場合、http()メソッド呼び出しも短絡され、空の配列を含むobservableが返されます。

    サービスがその機能をサポートするまで、HeroSearchService Observableをキャンセルしても保留中のHTTP要求は実際には中止されません。今のところ、望ましくない結果は破棄されます。
    “““

    ・ catchは失敗したobservableを傍受します。簡単な例では、エラーをコンソールに出力します。次に、検索結果をクリアするには、空の配列を含むobservableを返します。

    Import RxJS operators

    ほとんどのRxJS演算子は、AngularのObservableベースの実装には含まれていません。基本実装には、Angular自体に必要なものだけが含まれています。

    より多くのRxJS機能が必要な場合は、定義されているライブラリをインポートしてObservableを拡張します。このコンポーネントに必要なすべてのRxJSインポートは次のとおりです。

    src/app/hero-search.component.ts (rxjs imports)

    import { Observable }        from 'rxjs/Observable';
    import { Subject }           from 'rxjs/Subject';
    
    // Observable class extensions
    import 'rxjs/add/observable/of';
    
    // Observable operators
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/operator/debounceTime';
    import 'rxjs/add/operator/distinctUntilChanged';

    インポートの ‘rxjs / add / …’構文はよく知られていないかもしれません。 {{}}中括弧の間にあるシンボルの通常のリストはありません。

    演算子シンボル自体は必要ありません。いずれの場合も、ライブラリをインポートする単なる行為は、ライブラリのスクリプトファイルをロードして実行し、オブザーバブルクラスに演算子を追加します。

    Add the search component to the dashboard

    ヒーロー検索のHTML要素をDashboardComponentテンプレートの下部に追加します。

    src/app/dashboard.component.html

    <h3>Top Heroes</h3>
    <div class="grid grid-pad">
      <a *ngFor="let hero of heroes"  [routerLink]="['/detail', hero.id]"  class="col-1-4">
        <div class="module hero">
          <h4>{{hero.name}}</h4>
        </div>
      </a>
    </div>
    <hero-search></hero-search>

    最後に、heroSearchComponentをhero-search.component.tsからインポートし、それを宣言配列に追加します。

    src/app/app.module.ts (search)

      declarations: [
        AppComponent,
        DashboardComponent,
        HeroDetailComponent,
        HeroesComponent,
        HeroSearchComponent
      ],

    アプリをもう一度実行してください。ダッシュボードで、検索ボックスにテキストを入力します。既存のヒーロー名と一致する文字を入力すると、次のように表示されます。

    App structure and code

    このページの live example / downloadable example のサンプルソースコードを確認してください。次の構造を持っていることを確認します。

    angular-tour-of-heroes
    ├──src
    │ ├──app
    │ │  ├──app.component.ts
    │ │  ├──app.component.css
    │ │  ├──app.module.ts
    │ │  ├──app-routing.module.ts
    │ │  ├──dashboard.component.css
    │ │  ├──dashboard.component.html
    │ │  ├──dashboard.component.ts
    │ │  ├──hero.service.ts
    │ │  ├──hero.ts
    │ │  ├──hero-detail.component.css
    │ │  ├──hero-detail.component.html
    │ │  ├──hero-detail.component.ts
    │ │  ├──hero-search.component.html (new)
    │ │  ├──hero-search.component.css (new)
    │ │  ├──hero-search.component.ts (new)
    │ │  ├──hero-search.service.ts (new)
    │ │  ├──hero.service.ts
    │ │  ├──heroes.component.css
    │ │  ├──heroes.component.html
    │ │  ├──heroes.component.ts
    │ │  └──in-memory-data.service.ts (new)
    │ ├──main.ts
    │ ├──index.html
    │ ├──styles.css
    │ ├──systemjs.config.js
    │ └──tsconfig.json
    ├──node_modules ...
    └──package.json

    Home Stretch

    あなたはあなたの旅の終わりにおり、あなたは多くを成し遂げました。

    ・ アプリケーションでHTTPを使用するために必要な依存関係を追加しました。

    ・ あなたはHeroServiceをリファクタリングして、Web APIからヒーローをロードします。

    ・ post()、put()、およびdelete()メソッドをサポートするようにHeroServiceを拡張しました。

    ・ ヒーローの追加、編集、削除を可能にするようにコンポーネントを更新しました。

    ・ インメモリWeb APIを設定しました。

    ・ Observablesの使い方を学びました。

    このページで追加または変更したファイルは次のとおりです。

    公式ページ参照

    Next step

    このチュートリアルで見つかった概念とプラクティスの詳細を読むことができる learning pathに戻ります。

    • RSSを登録する

    • follow us in feedly

    Graphical FrontEnd Engineer
    - Daisuke Takayama

    MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
    現在、港区六本木で活動中。

    WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。