【Angular】公式チュートリアルの理解４ (データの管理②)

# はじめに
https://qiita.com/s-umbys/items/46b2b3075009d0217468

前記事「データの管理」セクションが非常にボリュームがあるため2記事に分けて進めていく、ということで前半部分のServiceについて、DIについてそれぞれ理解を深め、ルーティングの2つめの事例の設定を行った。

今回は少し毛色が異なり、`HttpClient`の設定を行っていく。HttpClientはhttpプロトコルを使った外部APIからデータを取得し、アプリケーションに提供するためのものでAngularに標準で備わっているものである。
ひとまずHTTP通信で外部からアプリケーションにデータを持ってくるときに必要な設定とでも認識するので十分であると思う。新しいことが出てくるがそういうもの、そういう設定が必要なんだと理解できれば問題なく、都度調べて使えるように慣れば良いと思う。

# 配送料金の取得
今回は固定値である配送料金が記述された外部の下記ファイルのデータを持ってきて表示できるように設定を行う。
```json:src/assets/shipping.json
[
  {
    "type": "Overnight",
    "price": 25.99
  },
  {
    "type": "2-Day",
    "price": 9.99
  },
  {
    "type": "Postal",
    "price": 2.99
  }
]
```

## HttpClientを使用できるようにAppModuleを設定する
HttpClientをアプリで使用できるようにするために必要な、ルートモジュールであるapp.moduleへの2つの設定を行う。
これは毎回こういう設定を行うものと分かれば問題ない。

```ts:src/app/app.module.ts
//1つめの設定
import { HttpClientModule } from '@angular/common/http';
//2つめの設定。下記へ追記
@NgModule({
  imports: [
    BrowserModule,
    //ここに追記する
    HttpClientModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
      { path: 'cart', component: CartComponent },
    ])
  ],
  declarations: [
    AppComponent,
    TopBarComponent,
    ProductListComponent,
    ProductAlertsComponent,
    ProductDetailsComponent,
    CartComponent,
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }
```

## CartServiceがHttpClientを使用できるように設定する
続いて、各構成単位ごとにHttpClientが使えるように設定を行う。今回はServiceであるCartServiceに設定する。ここは名前がHttpClientであるということで、記述方法や意味にこれまでと変わったことはない。

```ts:src/app/cart.service.ts
import { Injectable } from '@angular/core';
//HttpClientをimportする
import { HttpClient } from '@angular/common/http';
import { Product } from './products';

export class CartService {
  items: Product[] = [];
    //HttpClientをDIする(インスタンス化して使えるようにする)
  constructor(
    private http: HttpClient
  ) {}
/* . . . */
}
```

## 配送料を取得するためにCartServiceを設定する
外部ファイル(今回はshipping.json)からデータを取得するには`HttpClientのget()メソッド`を利用する。ここではHttpClientの get() メソッドを利用した getShippingPrices() メソッドを定義。
```ts:src/app/cart.service.ts
export class CartService {
/* . . . */
  getShippingPrices() {
    //getの<>の中身はgetするものの型を指定している。→「string型のtype,number型のpriceを要素に持つ配列」
    //※jsonファイルを確認すればわかる
    //その次のかっこはファイルの場所を指定している
    return this.http.get<{type: string, price: number}[]>('/assets/shipping.json');
  }
}
```


# 配送コンポーネントを作成する
外部から取得するコードを書いたら配送料を表示するためのコンポーネントを作成する。
```sh:ターミナルでの実行
ng generate component shipping
```
公式ドキュメントにあるように、これまで通り、selector、html、cssの設定が自動で行われていること、app.moduleのdeclarationsにShippingComponentとして追記されていることを確認する。

また、当コンポーネントのルーティング設定も下記のように行う。
```ts:src/app/app.module.ts
@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
      { path: '', component: ProductListComponent },
      { path: 'products/:productId', component: ProductDetailsComponent },
      { path: 'cart', component: CartComponent },
        //ここの設定。もう理解はできていると思う
      { path: 'shipping', component: ShippingComponent },
    ])
  ],
  declarations: [
    AppComponent,
    TopBarComponent,
    ProductListComponent,
    ProductAlertsComponent,
    ProductDetailsComponent,
    CartComponent,
    //自動で追加される部分
    ShippingComponent
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }
```

## ShippingComponent が CartService を使用するように設定する
上記の[配送料を取得するためにCartServiceを設定する](#配送料を取得するためにcartserviceを設定する)で設定したgetShippingPrices()を実行できるようにして、HTTP経由でデータを取得する設定を行う。

getShippingPrices()を実行するために、CartServiceを利用できるようにする。これまで通り、CartServiceをimportしてDIをする。（わからなくなったら戻って理解しよう）

ここで新しく`import { Observable } from 'rxjs';`というコードが出てくるがとりあえず入力し、後ほど説明する。
```ts:src/app/shipping/shipping.component.ts
import { Component, OnInit } from '@angular/core';
//ここを新しく追記
import { Observable } from 'rxjs';
import { CartService } from '../cart.service';

//ここ追記　DIしている
constructor(private cartService: CartService) { }
```

そして取得したデータをプロパティに代入する。
```ts:src/app/shipping/shipping.component.ts
export class ShippingComponent implements OnInit {

  shippingCosts!: Observable<{ type: string, price: number }[]>;

  ngOnInit(): void {
    this.shippingCosts =  this.cartService.getShippingPrices();
  }
}
```
後ほど説明する、といいった部分も含めてここでの理解を記す。
上記shippingCostsプロパティを見てわかるように`Observable`というAngular特有の型が存在する。Observableは`rxjs`というライブラリに属しているためimport文にそのように記述している。
ここでは、Observableという型がある一定の自由度を持つデータ渡しのための型だと理解すれば良いと思う。自由度を持つというのは、中身の型を[配送料を取得するためにCartServiceを設定する](#配送料を取得するためにcartserviceを設定する)で指定したように`<>`で中身の型を指定できる。これもjsonの中身の型と一致していることが簡単に確認できる。
型の中身の型を指定するという少し混乱する内容ではあるが、そういうものだと理解するしか無い。
※Observableはangularの中で非常に重要なものとなっているので具体的な使い方や説明は調べるといい。

以上のようなshippingCostsプロパティにgetShippingPrices()メソッドを用いてデータを代入する。この代入はngOnInit()メソッドの中に書かれており、ShippingComponentクラスが生成された後に、最初に実行される(値が代入される)ということになる。

```html:src/app/shipping/shipping.component.html
<h3>Shipping Prices</h3>

<div class="shipping-item" *ngFor="let shipping of shippingCosts | async">
  <span>{{ shipping.type }}</span>
  <span>{{ shipping.price | currency }}</span>
</div>
```
こうして表示設定を行う。これまで同様*ngForを用いて配列の中身を表示する。
ここで新しく`async`パイプが出てくるが、これはshippingCosts配列の中に値が存在する限り繰り返すという意味らしい。少し調べるとObservableを表示する際に必要なパイプのようだ。（逆にObservableはasyncパイプがないと表示できない？未確認だが）

```html:src/app/cart/cart.component.html
<h3>Cart</h3>
<p>
  <a routerLink="/shipping">Shipping Prices</a>
</p>
<div class="cart-item" *ngFor="let item of items">
  <span>{{ item.name }}</span>
  <span>{{ item.price | currency }}</span>
</div>
```
最後にリンクの設定を行う。
カートの中にShipping Pricesのリンクが作成されればOKで、そのリンクの先にドキュメントどおりの画面が出れば本セクションは完了である。

# おわりに
Angular公式チュートリアルの4として`データの管理`セクションを最後まで終了した。今回はHttpClientの使い方の全体像がなんとなく理解できれば良いと思う。Observableという話も出てきており、実際のangularでは大変重要なので、細かく知りたいときは調べてみてほしい。

下記リンクより、引き続きチュートリアルを進めていきたい。
次→[【Angular】公式チュートリアルの理解5 (ユーザー入力フォーム)](https://qiita.com/s-umbys/items/adaae2e8b3e66a941c70 "【Angular】公式チュートリアルの理解5 (ユーザー入力フォーム)")