らいふうっどの閑話休題

興味のあることをゆる~く書いていく

Angular 9/8 Tutorial: Build a Web App with HttpClient and RxJS の翻訳

itnextAngular 9/8 Tutorial: Build a Web App with HttpClient and RxJS の翻訳です。

f:id:ic_lifewood:20200104014414j:plain

この Angular 9 チュートリアルでは、 REST API の作成/シミュレーション、新しいプロジェクトの足場、重要なAPIの設定、最終的なアプリケーションの構築とデプロイから必要なすべてのステップを経て Angular 9 サンプルアプリケーションをクラウドへ構築する方法を学びます。

  • 例として、URLクエリ文字列とパラメーターを使用して GET リクエストを送信し、 JSON データを取得および使用するために Httplient を使用して、 Angular 9/8 アプリケーションの REST API サーバーから HTTP 応答を処理する方法、 HTTP エラーのエラー処理方法を学習します RxJS throwError() および catchError() 演算子、RxJS retry() および takeUntil() 演算子を使用して、劣悪なネットワーク接続で失敗した HTTP 要求を再試行し、保留中の要求をキャンセルする方法、最後に最新の Angular 8.3以上の機能です。
  • また、 Angular サービスと RxJS Observables の使用方法を確認し、プロジェクトで Angular Material を設定し、 Material Design コンポーネントで UI をスタイルする方法を学びます。
  • Angular 8.3+ の新しい ng デプロイ機能を使用して、 Angular 9 アプリケーションをコマンドラインから Firebase ホスティングに簡単にデプロイする方法について説明します。

現在、Angular 9 は RC バージョンであり、特に新しい Ivy レンダラーをはじめとするさまざまな新機能と改善が含まれています。

このチュートリアルは、最新のAngular 9バージョンに更新されました。

注:HTTP クライアント API の改良版である HttpClient を使用していることに注意してください。これは、 Angular バージョン 4.3.0-rc.0 以降で利用可能です。 古い HTTP クライアントは、Angular 9では使用できません。

このチュートリアルでは、 Angular 9 で HttpClient を使用して、サードパーティREST API から JSON データを取得するニュースアプリケーションを構築する方法も確認できます。

このステップバイステップのAngular 9チュートリアルでは、@angular/common/http パッケージから入手できる HttpClient を使用して、get() メソッドを使用して HTTP GET リクエストを作成する方法の実用的な例を見ていきます。

mid_content 143について説明

  • 疑似動作機能する作成し、 JSON REST API を完成させる方法、
  • Angular CLI v9 のインストール方法、
  • Angular CLI を使用して Angular 9 プロジェクトを作成する方法、
  • Angular Material をセットアップし、Material Design でアプリケーションをスタイルする方法、
  • Angular コンポーネントの作成方法、それらの間のルーティングとナビゲーション、
  • Angular サービスを作成および注入する方法、
  • HttpClient を使用してサーバーにHTTP GETリクエストを送信する方法、
  • HttpParams クラスを使用して、HttpRequest に URL クエリ文字列を追加する方法、
  • HttpClient によって返された RxJS Observables をサブスクライブおよびサブスクライブ解除する方法、
  • throwError() および catchError() 演算子を使用して HTTP エラーを処理する方法、
  • RxJS retry() 演算子を使用して失敗した HTTP 要求を再試行する方法、
  • リクエストがキャンセルされたときに takeUntil() 演算子を使用して、 HttpClient メソッドから返された RxJS Observables を unsubscribe する方法、
  • Angular 8.3+ から利用可能な新しい ng deploy コマンドを使用して、実稼働用のアプリケーションをビルドし、 Firebase ホスティングにデプロイする方法

この Angular 9 チュートリアルの手順は次のとおりです。

  • ステップ01 — Angular CLI v9 のセットアップ
  • ステップ02 — 新しい Angular 9 サンプルプロジェクトの初期化
  • ステップ03 — 疑似 JSON REST API をセットアップする
  • ステップ04 — サンプルプロジェクトで Angular HttpClient v9 を設定する
  • ステップ05 — Angular 9 コンポーネントの作成
  • ステップ06 — Angular 9 ルーティングを追加する
  • ステップ07 — Angular Material v9 を使用して UI をスタイル設定する
  • ステップ08 — Angular HttpClient v9 で JSON REST API を使用する
  • ステップ09 — RxJS catchError() および HttpClient を使用した HTTP エラー処理の追加
  • ステップ10 — RxJS retry() および HttpClient を使用して失敗した HTTP リクエストを再試行する
  • ステップ11 — RxJS takeUntil() を使用した HttpClient Observablesからのサブスクライブ解除
  • ステップ12 — HttpClient get() メソッドに URL クエリパラメーターを追加する
  • ステップ13 — Angular HttpClient v9 で完全な HTTP レスポンスを取得する
  • ステップ14 — Angular HttpClient v9 で型指定された HTTP 応答を要求する
  • ステップ15 — Angular 9 アプリケーションを Firebase Hosting にビルドおよびデプロイする

Angular HttpClient 、その機能、およびそれを使用する理由を紹介することから始めましょう。

Angular HttpClient とは何ですか?

Angular などのフレームワークを使用して構築されたフロントエンドアプリケーションは、 XMLHttpRequest インターフェイスまたは fetch() API を使用して、 REST API ( HTTP プロトコルに基づく)を介してバックエンドサーバーと通信します。

Angular HttpClient は、最新のブラウザーとレガシーブラウザーの両方をサポートする XMLHttpRequest インターフェイスを利用します。

HttpClient は @angular/common/http パッケージから入手でき、簡単なAPIインターフェイスと、簡単なテスト容易性、型指定された要求および応答オブジェクト、要求および応答インターセプター、RxJS Observables を備えたリアクティブ API 、合理化されたエラー処理などの強力な機能を備えています。

Angular HttpClient を使用する理由

HttpClient ビルトインサービスは、 Angular 開発者に多くの利点を提供します。

  • HttpClient を使用すると、 HTTP 要求と応答を簡単に送信および処理できます。
  • HttpClient には、テストユニットを実装するための多くの組み込み機能があり、
  • HttpClient は、次のような一般的な Web 開発タスクを簡素化する Promise の代わりに、 RxJS Observables を使用して非同期操作を処理します。
    • HTTP リクエストの集中、
    • ダウンロードおよびアップロード操作の進行を聞いて、
    • 簡単なエラー処理、
    • 失敗したHTTP要求などの再試行

HttpClient を紹介した後、 Angular 9 チュートリアルを正常に完了するために必要な前提条件からサンプルアプリケーションの構築に進みましょう。

前提条件

始める前に、いくつかの前提条件が必要です。

  • TypeScript の基本的な知識。 特に、TypeScript クラスやデコレータなどのオブジェクト指向の概念に精通している。
  • NPM 6+ がインストールされた Node10+ を備えたローカル開発マシン。 最近のほとんどのフロントエンドツールのように、 Angular CLI にはノードが必要です。 公式 Web サイトのダウンロードページにアクセスして、オペレーティングシステムのバイナリをダウンロードするだけです。 パッケージマネージャを使用してNodeをインストールする方法については、特定のシステム手順を参照することもできます。 ただし、推奨される方法は、 NVM — Node Version Manager — POSIX 準拠の bash スクリプトを使用して、複数のアクティブな Node.js バージョンを管理することです。

注:Angular開発用のローカル環境をインストールしたくないが、このチュートリアルのコードを試してみたい場合は、Angular CLI と互換性のあるAngularプロジェクトを作成するために使用できるフロントエンド開発用のオンラインIDEであるStackblitzを使用できます。

以前の前提条件がある場合、Angular HttpClient を使用して JSON データと catchError() などのさまざまな RxJS オペレーターを取得するための HTTP GET リクエストを送信する方法を例として説明する、 Angular 9 チュートリアルの次のステップに進む準備ができています。 エラー処理、失敗した HTTP 要求の再試行、保留中の要求のキャンセルなどの高度な機能を実装するための tap()retry() 、および takeUntil()

チュートリアルの最初のステップでは、 Angular CLI 9 をインストールし、ゼロからサンプルプロジェクトを作成する方法を見ていきます。

ステップ01 — Angular CLI v9 のセットアップ

この手順では、最新の Angular CLI 9 バージョンをインストールします(このチュートリアルの執筆時点)。

注:これらの手順は、 Angular 8 でも有効です。

f:id:ic_lifewood:20200104052525j:plain

Angular CLI は、 Angular プロジェクトを初期化して操作するための公式ツールです。 それをインストールするには、新しいコマンドラインインターフェイスを開き、次のコマンドを実行します。

$ npm install -g @angular/cli@next

このチュートリアルを書いている時点で、 angular/cli v9.0.0-rc.2 がシステムにインストールされます。

Angular 9 が正式にリリースされるまで、 @next タグを使用して最新のプレリリースバージョンをインストールする必要があることに注意してください。

ng version コマンドを実行すると、同様の出力が得られます。

Angular CLI: 9.0.0-rc.2
Node: 10.16.3
OS: win32 ia32
Angular:
...Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.900.0-rc.2
@angular-devkit/core         9.0.0-rc.2
@angular-devkit/schematics   9.0.0-rc.2
@schematics/angular          9.0.0-rc.2
@schematics/update           0.900.0-rc.2
rxjs                         6.5.3

次のステップでは、コマンドラインから新しいサンプルプロジェクトを初期化する方法を学習します。

ステップ02 — 新しい Angular 9 サンプルプロジェクトの初期化

このステップでは、サンプルプロジェクトの作成に進みます。 コマンドラインインターフェイスに戻り、次のコマンドを実行します。

$ cd ~
$ ng new angular-httpclient-example

CLIからいくつか質問があります。 Angular ルーティングを追加しますか? [はい]の y と入力し、どのスタイルシート形式を使用しますか? CSS を選択します。

これにより、プロジェクトにルーティングを自動的に設定するようにCLIに指示されるため、アプリケーションにナビゲーションを実装するためにコンポーネントのルートを追加するだけで済みます。

プロジェクトのフォルダー内で ng version コマンドを実行すると、同様の出力が得られます。

Angular CLI: 9.0.0-rc.2
Node: 10.16.3
OS: win32 ia32
Angular: <error>
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... routerPackage                         Version
---------------------------------------------------------
@angular-devkit/architect       0.900.0-rc.2 (cli-only)
@angular-devkit/build-angular   <error>
@angular-devkit/core            9.0.0-rc.2 (cli-only)
@angular-devkit/schematics      9.0.0-rc.2 (cli-only)
@schematics/angular             9.0.0-rc.2 (cli-only)
@schematics/update              0.900.0-rc.2 (cli-only)
rxjs                            6.5.3 (cli-only)
typescript                      3.6

次に、プロジェクトのフォルダーに移動し、次のコマンドを使用してローカル開発サーバーを実行します。

$ cd angular-httpclient-example
$ ng serve

ローカル開発サーバーは、 http://localhost:4200/ アドレスでリッスンを開始します。

f:id:ic_lifewood:20200104055526j:plain

Webブラウザーを開き、 http://localhost:4200/ アドレスに移動して、アプリが稼働していることを確認します。 これはこの時点でのスクリーンショットです。

f:id:ic_lifewood:20200104055653j:plain

ここで、開発サーバーを実行したままにして、次の手順の CLI コマンドを実行するための新しいコマンドラインインターフェイスを開始する必要があります。

次のステップでは、 Angular サンプルアプリケーションで使用する疑似 JSON REST API を作成する方法を学習します。

ステップ03 — 疑似 JSON REST API をセットアップする

Angularアプリケーションの開発に進む前に、 HttpClient を使用して JSON REST API を準備する必要があります。

サードパーティREST API サーバーから JSON データを消費または取得することもできますが、この例では、疑似 REST API を作成することを選択します。 実際の REST API の例については、このチュートリアルをご覧ください。 Angular に関する限り、疑似のAPIと実際の REST API の使用に違いはありません。

前述のように、外部 API サービスを使用するか、実際の REST API サーバーを作成するか、 json-server を使用して疑似 API を作成できます。 この例では、最後のアプローチを使用します。

したがって、新しいコマンドラインインターフェイスに戻り、プロジェクトの npm から json-server をインストールすることから始めます。

$ cd ~/angular-httpclient-example
$ npm install --save json-server

次に、Angularプロジェクトのルートフォルダーに サーバー フォルダーを作成します。

$ mkdir server
$ cd server

サーバー フォルダーで、 database.json ファイルを作成し、次の JSON オブジェクトを追加します。

{
    "products": []
}

この JSON ファイルは、 REST API サーバーのデータベースとして機能します。 REST API で提供するデータを追加するか、 Faker.js を使用して大量の現実的な疑似データを自動的に生成できます。

コマンドラインに戻り、サーバー フォルダーから戻って、次のコマンドを使用してnpmから Faker.js をインストールします。

$ cd ..
$ npm install faker --save

この例を作成すると、 faker v4.1.0 がインストールされます。

次に、 generate.js ファイルを作成し、次のコードを追加します。

var faker = require('faker');
var database = {
  products: []
};

for (var i = 1; i<= 300; i++) {
  database.products.push({
    id: i,
    name: faker.commerce.productName(),
    description: faker.lorem.sentences(),
    price: faker.commerce.price(),
    imageUrl: "https://source.unsplash.com/1600x900/?product",
    quantity: faker.random.number()
  });
}
console.log(JSON.stringify(database));

最初に faker をインポートし、次に製品の空の配列を1つ持つオブジェクトを定義しました。 次に、forループに入って、製品名を生成するために faker.commerce.productName() などの faker メソッドを使用して300の疑似エントリを作成しました。 利用可能なすべての方法を確認してください。 最後に、データベースオブジェクトを文字列に変換し、標準出力に記録しました。

次に、 generateserver スクリプトpackage.json ファイルに追加します。

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "generate": "node ./server/generate.js > ./server/database.json",
    "server": "json-server --watch ./server/database.json"
  },

次に、コマンドラインインターフェイスに戻り、次のコマンドを使用して generate スクリプトを実行します。

$ npm run generate

最後に、次のコマンドを実行して REST API サーバーを実行します。

$ npm run server

通常の REST API サーバーと同じように、サーバーに HTTP リクエストを送信できるようになりました。 サーバーは http://localhost:3000/ アドレスから利用できます。

これらは、 JSON REST API サーバーを介して使用できる API エンドポイントです。

  • GET /products 製品取得
  • GET /products/<id> 製品取得 id 別
  • POST /products 製品新規作成
  • PUT /products/<id> 製品更新 id 毎
  • PATCH /products/<id> 製品部分更新 id 毎
  • DELETE /products/<id> 製品削除 id 毎

_page および _limit パラメーターを使用して、ページ分割されたデータを取得できます。 リンクヘッダーには、 最初最後リンク が表示されます。

例:

データの最初のページを取得するには GET /products?_page=1 、 データの最初のページの最初の5つの製品を取得するには、 GET /products?_page=1&_limit=5

注:フィルター、ソート、順序付けなどの他の機能を使用できます。 詳細については、ドキュメントをご覧ください。

JSON REST API サーバーを実行したままにして、次の手順のコマンドを入力するための新しいコマンドラインインターフェイスを開きます。

これまでの作業の概要として、 Angular CLI をインストールし、最新の Angular 9 バージョンに基づいて新しいプロジェクトを初期化しました。 次に、 JSON ファイルに基づいて json-server を使用して REST API を作成しました。 Angular 9 チュートリアルの次のステップでは、 Angular 9 プロジェクトで HttpClient を設定する方法を学びます。

ステップ04 — サンプルプロジェクトで Angular HttpClient v9 を設定する

このステップでは、この例の HttpClient モジュールのセットアップに進みます。

HttpClient は別の Angular モジュールにあるため、使用する前にメインアプリケーションモジュールにインポートする必要があります。

コードエディターまたはIDEでサンプルプロジェクトを開きます。 Visual Studio Code を使用します。

次に、 src/app/app.module.ts ファイルを開き、HttpClientModule をインポートして、次のようにモジュールの imports 配列に追加します。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

以上で、プロジェクトで HttpClient サービスを使用する準備が整いましたが、その前にいくつかのコンポーネント(ホームコンポーネントコンポーネント)を作成する必要があります。 これは、次のステップで行うことを学ぶものです。

ステップ05 — Angular 9 コンポーネントの作成

このステップでは、アプリケーション UI を制御する Angular コンポーネントの作成に進みます。

新しいコマンドラインインターフェイスに戻り、次のコマンドを実行します。

$ cd ~/angular-httpclient-example
$ ng generate component home

これはコマンドの出力です:

CREATE src/app/home/home.component.html (19 bytes)
CREATE src/app/home/home.component.spec.ts (614 bytes)
CREATE src/app/home/home.component.ts (261 bytes)
CREATE src/app/home/home.component.css (0 bytes)
UPDATE src/app/app.module.ts (467 bytes)

CLI は、コンポーネント用に4つのファイルを作成し、 src/app/app.module.ts ファイルの declarations 配列に追加しました。

次に、次のコマンドを使用してaboutコンポーネントを作成しましょう。

$ ng generate component about

次に、 src/app/about/about.component.html を開き、次のコードを追加します。

<p style="padding: 13px;">
An Angular 9 example application that demonstrates how to use HttpClient to consume REST APIs
</p>

次の手順でホームコンポーネントを更新します。

Angular 9 チュートリアルの次のステップでは、これらのコンポーネントルーターに追加します。

ステップ06 — Angular 9 ルーティングを追加する

このステップでは、例にルーティングを追加します。

ルーティング構成用に Angular CLI によって自動的に作成された src/app/app-routing.module.ts ファイルに戻り、コンポーネントをインポートして、次のようにルートを追加します。

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full'},
  { path: 'home', component: HomeComponent },
  { path: 'about', component: AboutComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

最初にhome と about コンポーネントについてインポートし、次に空のパスをホームコンポーネントにリダイレクトするためのルートを含む 3 つのルートを追加しました。これにより、ユーザーがアプリにアクセスすると、ホームページにリダイレクトされます。

この例の次のステップでは、UIスタイルを設定するためにプロジェクトに Angular Material を設定します。

ステップ07 — Angular Material v9 を使用して UI をスタイル設定する

Angular 9 チュートリアルのこの手順では、 Angular Material をプロジェクトに追加し、アプリケーション UI のスタイルを設定します。

Angular Material は、開発者がプロフェッショナルな UI を作成できる Material Design コンポーネントを提供します。 Angular CLI v7 + の新しい ng add コマンドを使用すると、プロジェクトで Angular Material を簡単に設定できます。

コマンドラインインターフェイスに戻り、プロジェクトのルートから次のコマンドを実行します。

$ ng add @angular/material

テーマを選択するよう求められます。 Indigo/Pink を選択します。

その他のオプション—ジェスチャー認識用に HammerJS をセットアップしますか? Angular Material のブラウザアニメーションを設定しますか? キーボードの Enter キーを押すだけで、デフォルトの回答を選択できます。

次に、 src/styles.css ファイルを開き、テーマを追加します。

@import "~@angular/material/prebuilt-themes/indigo-pink.css";

各 Angular Material コンポーネントには、コンポーネントを使用する前にインポートする必要がある個別のモジュールがあります。 src/app/app.module.ts ファイルを開き、次のインポートを追加します。

import { MatToolbarModule,
  MatIconModule,
  MatCardModule,
  MatButtonModule,
  MatProgressSpinnerModule } from '@angular/material';

次のモジュールをインポートしました。

  • MatToolbar ヘッダー、タイトル、またはアクションのコンテナーの提供
  • MatCard 単一の件名のコンテキストでテキスト、写真、およびアクションのコンテンツコンテナーを提供
  • MatButton マテリアルデザインのスタイリングとインクの波紋で強化されたネイティブの <button> または <a> 要素を提供
  • MatProgressSpinner 進行状況とアクティビティの循環インジケータを提供

次に、これらのモジュールを imports 配列に含める必要があります。

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    BrowserAnimationsModule,
    MatToolbarModule,
    MatIconModule,
    MatButtonModule,
    MatCardModule,
    MatProgressSpinnerModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

次に、 src/app/app.component.html ファイルを開き、次のように更新します。

<mat-toolbar color="primary">
  <h1>
    ngStore 
  </h1>
  <button mat-button routerLink="/">Home</button>
  <button mat-button routerLink="/about">About</button>
</mat-toolbar>
<router-outlet></router-outlet>

ホームとコンポーネントに関する2つのナビゲーションボタンを備えたトップバーを含むアプリケーションのシェルを作成しました。

チュートリアルのこの時点までに行ったことの概要として、プロジェクトに HttpClient と Angular Material v9 をセットアップし、ホームとコンポーネントについて作成し、ルーティングを構成し、最後にナビゲーション付きのトップバーを含むアプリケーションのシェルを追加しました。

チュートリアルの次のステップでは、 HttpClient v9 を使用して REST API サーバーから JSON データを取得する方法を学習します。

ステップ08 — Angular HttpClient v9 で JSON REST API を使用する

このステップでは、サンプルアプリケーションの REST API サーバーから JSON データを使用します。

REST API サーバーからのデータの消費を処理するコードをカプセル化するために、 Angular サービスを作成する必要があります。

サービスは、 Angular 依存性注入を使用して他のサービスおよびコンポーネントによって注入できるシングルトンです。

ソフトウェアエンジニアリングでは、依存性注入は、あるオブジェクトが別のオブジェクトの依存性を提供する手法です。 ソース

次に、 JSON REST API と連動する Angular サービスを生成します。 コマンドラインインターフェイスに戻り、次のコマンドを実行します。

$ ng generate service data

次に、 src/app/data.service.ts ファイルを開き、次のように HttpClient をインポートして注入します。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  private REST_API_SERVER = "http://localhost:3000";

  constructor(private httpClient: HttpClient) { }

}

HttpClient サービスをプライベート httpClient インスタンスとしてインポートおよび注入しました。 また、 REST API サーバーのアドレスを保持する REST_API_SERVER 変数を定義しました。

次に、 GET 要求を REST API エンドポイントに送信して JSON データを取得する sendGetRequest() メソッドを追加します。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  private REST_API_SERVER = "http://localhost:3000";

  constructor(private httpClient: HttpClient) { }

  public sendGetRequest(){
    return this.httpClient.get(this.REST_API_SERVER);
  }
}

このメソッドは、単に HttpClientget() メソッドを呼び出して、 GET 要求を REST API サーバーに送信します。

次に、ホームコンポーネントでこのサービスを使用する必要があります。 src/app/home/home.component.ts ファイルを開き、次のようにデータサービスをインポートして注入します。

import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  products = [];

  constructor(private dataService: DataService) { }

  ngOnInit() {
    this.dataService.sendGetRequest().subscribe((data: any[])=>{
      console.log(data);
      this.products = data;
    })  
  }
}

コンポーネントコンストラクターを使用して、プライベート DataService インスタンスとして dataService をインポートおよび注入しました。

次に、変数 products を定義し、 JSON REST API サーバーからデータを取得するためにサービスの sendGetRequest() メソッドを呼び出しました。

sendGetRequest() メソッドは、RxJS Observable である HttpClient.get() メソッドの戻り値を返すため、返された Observable にサブスクライブして、実際に HTTP GET 要求を送信し、 HTTP 応答を処理します。

データを受信後、 products 配列に追加しました。

次に、 src/app/home/home.component.html ファイルを開き、次のように更新します。

<div style="padding: 13px;">
  <mat-spinner *ngIf="products.length === 0"></mat-spinner>
  <mat-card *ngFor="let product of products" style="margin-top:10px;">
    <mat-card-header>
      <mat-card-title>{{product.name}}</mat-card-title>
      <mat-card-subtitle>{{product.price}} $/ {{product.quantity}}
      </mat-card-subtitle>
    </mat-card-header>
    <mat-card-content>
      <p>
        {{product.description}}
      </p>
      <img style="height:100%; width: 100%;" src="{{ product.imageUrl }}" />
    </mat-card-content>
    <mat-card-actions>
      <button mat-button> Buy product</button>
    </mat-card-actions>
  </mat-card>
</div>

products の配列の長さがゼロに等しい場合、つまり REST API サーバーからデータを受信しない前に、ロードスピナーを表示するために <mat-spinner> コンポーネントを使用しました。

次に、 products の配列を反復処理し、マテリアルカードを使用して各製品の名前価格数量説明画像を表示しました。

これは、JSON データが取得された後のホームページのスクリーンショットです。

次に、サービスにエラー処理を追加する方法を確認します。

ステップ09 — RxJS catchError() および HttpClient を使用した HTTP エラー処理の追加

この手順では、サンプルアプリケーションにエラー処理を追加します。

Angular の HttpClient メソッドは、エラーをキャッチして処理するための pipe() メソッドを介して Observables を返すため、RxJSの catchError() 演算子で簡単に使用できます。 サービス内のエラーを処理するメソッドを定義するだけです。

フロントエンドアプリケーションには 2 種類のエラーがあります。

  • ネットワークの問題、 JavaScript 構文およびタイプエラーなどのクライアント側エラー。 これらのエラーは ErrorEvent オブジェクトを返します。
  • サーバーのコードエラーやデータベースアクセスエラーなどのサーバー側エラー。 これらのエラーは HTTP エラー応答を返します。

そのため、エラーが適切に処理できるように、エラーのタイプを取得するには、エラーが ErrorEventインスタンスであるかどうかを確認するだけです。

それでは、例を見てみましょう。 src/app/data.service.ts ファイルを開き、それに応じて更新します。

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  private REST_API_SERVER = "http://localhost:3000/products";

  constructor(private httpClient: HttpClient) { }

  handleError(error: HttpErrorResponse) {
    let errorMessage = 'Unknown error!';
    if (error.error instanceof ErrorEvent) {
      // Client-side errors
      errorMessage = `Error: ${error.error.message}`;
    } else {
      // Server-side errors
      errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
    }
    window.alert(errorMessage);
    return throwError(errorMessage);
  }
  public sendGetRequest(){
    return this.httpClient.get(this.REST_API_SERVER).pipe(catchError(this.handleError));
  }
}

ご覧のとおり、これはアプリケーション内の各サービスに対して行う必要があります。この例では 1 つのサービスしか含まれていませんが、アプリケーションがエラーをスローする可能性のある多くのサービスで成長し始めると、代わりに、より良いソリューションを使用する必要があります。 エラーが発生しやすい各サービスごとに handleError メソッドを使用します。 1 つの解決策は、 HttpClient interceptors を使用して、 Angular アプリケーションでエラーをグローバルに処理することです。

これは、サーバーに到達できない場合のコンソール上のエラーのスクリーンショットです。

次のステップでは、失敗した HTTP リクエストの送信を自動的に再試行することにより、データサービスを改善する方法を確認します。

ステップ10 — RxJS retry() および HttpClient を使用して失敗した HTTP リクエストを再試行する

Angular 9 チュートリアルのこのステップでは、 HttpClient で RxJS の retry() 演算子を使用して、返された Observable を自動的に resubscribing し、失敗した HTTP リクエストを再送信する方法を説明します。

多くの場合、エラーは一時的なものであり、ネットワークの状態が悪いため、再試行するだけで自動的に消えます。 たとえば、モバイルデバイスではネットワークの中断が頻繁に発生するため、ユーザーが再試行すると、成功した応答が返される場合があります。 ユーザーに手動で再試行させるのではなく、サンプルアプリケーションで自動的に再試行する方法を見てみましょう。

RxJS ライブラリには、いくつかの retry 演算子が用意されています。 その中には、指定した回数だけ RxJS Observable に自動的に再サブスクライブできる retry() があります。 HttpClient メソッドから返されたObservable を re-subscribe すると、サーバーに HTTP リクエストが再送信されるため、ユーザーは操作を繰り返したり、アプリケーションをリロードしたりする必要がありません。

エラーハンドラーの前に HttpClient メソッドから返された Observable に( pipe() メソッドを使用して)パイプすることにより、RxJSの retry() を使用できます。

src/app/data.service.ts ファイルに移動して、 retry() をインポートします。

import { retry, catchError } from 'rxjs/operators';

次に、 sendGetRequest() メソッドを次のように更新します。

  public sendGetRequest(){
    return this.httpClient.get(this.REST_API_SERVER).pipe(retry(3), catchError(this.handleError));
  }

これにより、失敗した HTTP 要求の送信が3回再試行されます。

次のステップでは、サンプルのホームコンポーネントで RxJS Observables の unsubscribe する方法を確認します。

ステップ11 — RxJS takeUntil() を使用した HttpClient Observablesからの Unsubscribing

Angular 9 チュートリアルのこのステップでは、 takeUntil() を使用して、コードで Observables が必要な理由と unsubscribe する方法について学習します。

まず、 HttpClient メソッドによって返される Observables から unsubscribe する必要がありますか?

通常、 Angular コンポーネントのサブスクライブされた RxJS Observables を手動で unsubscribing してメモリリークを回避する必要がありますが、 HttpClient の場合、これは HTT P応答の受信時に unsubscribe することにより、 Angular によって自動的に処理されます。 ただし、ユーザーがコンポーネントを離れようとしているときに保留中のリクエストをキャンセルするなど、手動で unsubscribe する必要がある場合があります。

コンポーネントngOnDestroy() ライフサイクルメソッドの subscribe() メソッドによって返される Subscription オブジェクトから unsubscribe() メソッドを呼び出すだけで、 Observable からサブスクライブを解除できます。

また、 takeUntil() を使用して、 Observables を unsubscribe または完了するより良い方法があります。

takeUntil() は、通知オブジェクト Observable が値を発行するまで、ソース Observable が発行した値を発行します。

コンポーネントが破棄されたときにこの演算子を使用して Observables を完了する方法を見てみましょう。

保留中のすべての HTTP リクエストの angular 4+を cancel / unsubscribe する方法をご覧ください。

src/app/home/home.component.ts ファイルを開き、次のように更新します。

import { Component, OnInit, OnDestroy } from '@angular/core';
import { DataService } from '../data.service';
import {  takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, OnDestroy {

  products = [];
  destroy$: Subject<boolean> = new Subject<boolean>();

  constructor(private dataService: DataService) { }

  ngOnInit() {
    this.dataService.sendGetRequest().pipe(takeUntil(this.destroy$)).subscribe((data: any[])=>{
      console.log(data);
      this.products = data;
    })  
  }

  ngOnDestroy() {
    this.destroy$.next(true);
    // Unsubscribe from the subject
    this.destroy$.unsubscribe();
  }

}

最初に、 OnDestroy インターフェース、 Subject 、および takeUntil() 演算子をインポートしました。 次に、 OnDestroy インターフェイスを実装し、コンポーネントngOnDestroy() ライフサイクルフックを追加しました。

次に、我々は takeUntil() 演算子の通知として使用されるブール値(値のタイプが実際にこの例では重要ではない)を放出することができるテーマのインスタンスを作成しました。

次に、 ngOnInit() ライフサイクルフックで、データサービスの sendGetRequest() を呼び出し、返された Observable の pipe() メソッドを呼び出して takeUntil() 演算子をパイプ処理し、結合された Observable にサブスクライブします。 subscribe() メソッドの本体に、取得した HTTP 応答のデータを products 配列に入れるロジックを追加しました。

takeUntil() オペレーターは、通知オブザーバブルから値が発行されるまで、通知された Observable が値を発行できるようにします。

Angularはコンポーネントを破棄するときに、 OnDestroy ライフサイクルメソッドを呼び出します。この場合、このメソッド はnext() メソッドを呼び出して値を発行し、RxJS がすべての subscribe 済み Observable を完了するようにします。

それでおしまい。 このステップでは、 HTTP 応答を受信する前にユーザーがコンポーネントから移動することを決定した場合に、返された Observable から unsubscribing して保留中の HTTP 要求をキャンセルするロジックを追加しました。

Angular 9 チュートリアルの次のステップでは、 HttpClientget() メソッドで URL クエリパラメーターを使用する方法について説明します。

ステップ12 — HttpClient get() メソッドに URL クエリパラメーターを追加する

このステップでは、サンプルアプリケーションにページネーションを実装するためのロジックの追加を開始します。 fromString および HttpParams を介して URL クエリパラメーターを使用して、ページ分割されたデータを取得するためのJSON REST APIサーバーの /products エンドポイントの _page および _limit パラメーターに適切な値を提供する方法について説明します。

src/app/data.service.ts ファイルを開き、 HttpParams の次のインポートを追加して開始します。

import { HttpClient, HttpErrorResponse, HttpParams } from "@angular/common/http";

次に、 sendGetRequest() メソッドを次のように更新します。

  public sendGetRequest(){
    // Add safe, URL encoded_page parameter 
    const options = { params: new HttpParams({fromString: "_page=1&_limit=20"}) };
    return this.httpClient.get(this.REST_API_SERVER, options).pipe(retry(3), catchError(this.handleError));
  }

HttpParamsfromString を使用して、 _page=1&_limit=20 文字列からHTTPクエリパラメーターを作成しました。 これにより、20個の products の最初のページが返されます。

これで、 sendGetRequest() を使用してデータの最初のページを取得します。 受信した HTTP 応答には、データページの最初、前、次、および最後のリンクに関する情報を含むリンクヘッダーが含まれます。

リンクヘッダーには、最初、前、次、最後のリンクが表示されます。 次のステップでは、完全な HTTP 応答を解析してこれらのページネーションリンクを抽出する方法を確認します。

ステップ13 — Angular HttpClient v9 で完全な HTTP レスポンスを取得する

このサイトでは、 JSON REST API サーバーから受信した HTTP 応答に含まれる Link ヘッダーからページネーション情報を取得するロジックを実装することから始めます。

デフォルトでは、 HttpClient は応答本文のみを提供しますが、この場合、ページネーションリンクのリンクヘッダーを解析する必要があるため、 observe オプションを使用して完全な HttpResponse が必要であることを HttpClient に伝える必要があります。

HTTPのLinkヘッダーにより、サーバーは、関心のあるクライアントを、要求されたリソースに関するメタデータを含む別のリソースに向けることができます。 Wikipedia

src/app/data.service.ts ファイルに移動し、RxJS tap() 演算子をインポートします。

import { retry, catchError, tap } from 'rxjs/operators';

次に、次の文字列変数を定義します。

  public first: string = "";
  public prev: string = "";
  public next: string = "";
  public last: string = "";

次に、 Link ヘッダーを解析し、それに応じて前の変数を設定する parseLinkHeader() メソッドを定義します。

  parseLinkHeader(header) {
    if (header.length == 0) {
      return ;
    }    let parts = header.split(',');
    var links = {};
    parts.forEach( p => {
      let section = p.split(';');
      var url = section[0].replace(/<(.*)>/, '$1').trim();
      var name = section[1].replace(/rel="(.*)"/, '$1').trim();
      links[name] = url;    });    this.first  = links["first"];
    this.last   = links["last"];
    this.prev   = links["prev"];
    this.next   = links["next"]; 
  }

次に、 sendGetRequest() を次のように更新します。

  public sendGetRequest(){
      // Add safe, URL encoded _page and _limit parameters
      return this.httpClient.get(this.REST_API_SERVER, {  params: new HttpParams({fromString: "_page=1&_limit=20"}), observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
          console.log(res.headers.get('Link'));
          this.parseLinkHeader(res.headers.get('Link'));
        }));
  }

ヘッダー付きの完全な HTTP 応答を取得できるように、get() メソッドの options パラメーターに response 値を持つ observe オプションを追加しました。 次に、RxJS tap() 演算子を使用して Link ヘッダーを解析し、最後の Observable を返します。

sendGetRequest() が完全な HTTP 応答で Observable を返すようになったため、次のように src/app/home/home.component.ts ファイルを開いて HttpResponse をインポートするようにホームコンポーネントを更新する必要があります。

import { HttpResponse } from '@angular/common/http';

次に、 subscribe() メソッドを次のように更新します。

  ngOnInit() {
    this.dataService.sendGetRequest().pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>)=>{
      console.log(res);
      this.products = res.body;
    })  
  }

これで、受信した HTTP 応答の body オブジェクトのデータにアクセスできます。

次に、 src/app/data.service.ts ファイルに戻り、次のメソッドを追加します。

  public sendGetRequestToUrl(url: string){
    return this.httpClient.get(url, { observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
      console.log(res.headers.get('Link'));
      this.parseLinkHeader(res.headers.get('Link'));
    }));
  }

このメソッドは sendGetRequest() に似ていますが、 HTTP GET リクエストを送信する必要がある URL を取得する点が異なります。

src/app/home/home.component.ts ファイルに戻り、次のメソッドを定義して追加します。

  public firstPage() {
    this.products = [];
    this.dataService.sendGetRequestToUrl(this.dataService.first).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
      console.log(res);
      this.products = res.body;
    })
  }
  public previousPage() {    if (this.dataService.prev !== undefined && this.dataService.prev !== '') {
      this.products = [];
      this.dataService.sendGetRequestToUrl(this.dataService.prev).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
        console.log(res);
        this.products = res.body;
      })
    }
  }
  public nextPage() {
    if (this.dataService.next !== undefined && this.dataService.next !== '') {
      this.products = [];
      this.dataService.sendGetRequestToUrl(this.dataService.next).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
        console.log(res);
        this.products = res.body;
      })
    }
  }
  public lastPage() {
    this.products = [];
    this.dataService.sendGetRequestToUrl(this.dataService.last).pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<any>) => {
      console.log(res);
      this.products = res.body;
    })
  }  

最後に、 src/app/home/home.component.html ファイルを開いて追加し、次のようにテンプレートを更新します。

<div style="padding: 13px;">
  <mat-spinner *ngIf="products.length === 0"></mat-spinner>
    <mat-card *ngFor="let product of products" style="margin-top:10px;">
    <mat-card-header>
      <mat-card-title>#{{product.id}} {{product.name}}</mat-card-title>
      <mat-card-subtitle>{{product.price}} $/ {{product.quantity}}
      </mat-card-subtitle>
    </mat-card-header>
    <mat-card-content>
      <p>
        {{product.description}}
      </p>
      <img style="height:100%; width: 100%;" src="{{ product.imageUrl }}" />
    </mat-card-content>
    <mat-card-actions>
      <button mat-button> Buy product</button>
    </mat-card-actions>
  </mat-card>
</div>
<div>
  <button (click) ="firstPage()" mat-button> First</button>
  <button (click) ="previousPage()" mat-button> Previous</button>
  <button (click) ="nextPage()" mat-button> Next</button>
  <button (click) ="lastPage()" mat-button> Last</button>
</div>

これは、アプリケーションのスクリーンショットです。

ステップ14 — Angular HttpClient v9 で型指定された HTTP 応答を要求する

この手順では、サンプルアプリケーションで型指定された HTTP 応答を使用する方法について説明します。

Angular HttpClient を使用すると、要求オブジェクトで応答オブジェクトのタイプを指定できるため、応答を簡単かつ簡単に使用できます。 これにより、コンパイル時に型アサーションも有効になります。

まず、必要なプロパティを備えた TypeScript インターフェースを使用してカスタムタイプを定義します。

コマンドラインインターフェイスに戻り、プロジェクトのルートから次のコマンドを実行します。

$ ng generate interface  product

次に、 src/app/product.ts ファイルを開き、次のように更新します。

export interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
  quantity: number;
  imageUrl: string;
}

次に、データサービスで HttpClient.get() 呼び出しの type パラメーターとして Product インターフェースを指定します。 src/app/data.service.ts ファイルに戻って、 Product インターフェースをインポートします。

import { Product } from './product';

次:

  public sendGetRequest() {
    return this.httpClient.get<Product[]>(this.REST_API_SERVER, { params: new HttpParams({fromString: "_page=1&_limit=20"}), observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
      console.log(res.headers.get('Link'));
      this.parseLinkHeader(res.headers.get('Link'));
    }));
  }

  public sendGetRequestToUrl(url: string) {
    return this.httpClient.get<Product[]>(url, { observe: "response"}).pipe(retry(3), catchError(this.handleError), tap(res => {
      console.log(res.headers.get('Link'));
      this.parseLinkHeader(res.headers.get('Link'));
    }));
  }  

次に、 src/app/home/home.component.ts ファイルを開き、 Product インターフェイスをインポートします。

次に、 Product 配列のタイプを次のように変更します。

export class HomeComponent implements OnInit, OnDestroy {
  products: Product[] = [];

次に、 sendGetRequest() 呼び出しで HTTP 応答のタイプを変更します。

  ngOnInit() {
    this.dataService.sendGetRequest().pipe(takeUntil(this.destroy$)).subscribe((res: HttpResponse<Product[]>) => {
      console.log(res);
      this.products = res.body;
    })
  }

他の firstPage()previousPage()nextPage()lastPage() メソッドについても同じことを行う必要があります。

ステップ15 — Angular 9 アプリケーションを Firebase Hosting にビルドおよびデプロイする

このステップでは、 Angular 8.3+ で利用可能な ng deploy コマンドを使用して、サンプルアプリケーションをビルドして Firebase ホスティングにデプロイする方法を見ていきます。

疑似の JSON サーバーなしでフロントエンドアプリケーションをデプロイする方法のみを見ていきます。

Angular CLI 8.3+ では、プロジェクトに関連付けられた deploy CLI ビルダーを使用して、 Angular アプリケーションを以前より簡単にデプロイできる新しい ng deploy コマンドが導入されました。 さまざまなプラットフォームに展開機能を実装するサードパーティビルダーが多数あります。 ng add コマンドを実行して、プロジェクトにそれらを追加できます。

展開パッケージを追加すると、選択したプロジェクトの展開セクションでワークスペース構成(つまり、 angular.json ファイル)が自動的に更新されます。 その後、 ng deploy コマンドを使用して、そのプロジェクトをデプロイできます。

プロジェクトを Firebase ホスティングに展開することで、例を見てみましょう。

コマンドラインインターフェイスに戻り、 Angular プロジェクトのルートフォルダー内にいることを確認して、次のコマンドを実行します。

$ ng add @angular/fire

これにより、 Firebase 展開機能がプロジェクトに追加されます。

このコマンドは、次のセクションを追加して、プロジェクトの package.json も更新します。

  "deploy": {
    "builder": "@angular/fire:deploy",
    "options": {}
  }

CLI は、ここに認証コードを貼り付けるように求めます。デフォルトの Web ブラウザーを開き、 Firebase アカウントを管理するための Firebase CLI 権限を付与するように求めます。

Firebase アカウントに関連付けられた Google アカウントでサインインすると、認証コードが付与されます:

次に、プロンプトが表示されます:プロジェクトを選択してください:(矢印キーまたはタイプを使用して検索します)。 前に Firebase プロジェクトを作成しておく必要があります。

CLIfirebase.json および .firebaserc ファイルを作成し、それに応じて angle.json ファイルを更新します。

次に、次のコマンドを使用して、アプリケーションを Firebase にデプロイします。

$ ng deploy

このコマンドは、アプリケーションの最適化されたビルド( ng deploy --prod コマンドと同等)を生成し、実稼働アセットを Firebase ホスティングにアップロードします。

まとめ

この Angular 9 チュートリアルを通して、最新バージョンを使用した完全な Angular アプリケーションの実例を作成しました。

要約すると、 HttpClient を設定し、 HttpClient.get() メソッドを使用してパラメーターを指定して HTTP GET リクエストを送信する方法、 RxJS throwError() および catchError() 演算子を使用して HTTP エラーを処理する方法、 takeUntil() 演算子を使用してキャンセルされた HTTP リクエストの RxJS Observables から、 retry() 演算子で失敗したリクエストを再試行し、最後に Angular 8.3+ から利用可能な最新の ng deploy 機能を使用してアプリケーションを Firebase ホスティングにデプロイする方法

著者について

Ahmed Bouchefra は、5年以上の経験を持つ Web 開発者であり、ソフトウェア開発の工学学位を持つ技術著者です。 上記のリンクをクリックして彼を雇うか、LinkedIn アカウントから彼に連絡することができます。 彼は、SitePoint、Smashing、DigitalOcean、RealPython、freeCodeCamp、JScrambler、Pusher、Auth0などの業界をリードするWebサイトの技術コンテンツを作成しました。 彼はまた、 AmazonLeanpub から入手できる最新の Web 開発に関するさまざまな本を共著しています。

当初、2019年12月5日に https://www.techiediaries.com で公開されました。

itnext.io

itnext.io