Angularヒーローチュートリアル5

# サービス
サービスとは、データを取得しコンポーネントに提供する部分です。

## サービスの作成

```terminal:ターミナル
$ cd src/app
$ ng generate service hero
```

`hero.service.ts`と`hero.service.spec.ts`が生成されます。
`hero.service.ts`でデータの処理を行っていきます。

## データの取得
自動で生成されたコードに少し加えます。

```ts:hero.service.ts
import { Injectable } from '@angular/core';
//以下2行でヒーロー情報の型とデータをインポート
import {Hero} from './hero';
import {HEROES} from './mock-heroes';

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

  constructor() { }

  getHeroes(): Hero[]{ //ヒーローのデータを返す関数を定義
    return HEROES;
  }
}
```

## heroコンポーネントの修正
heroコンポーネントでデータをインポートしていましたが、サービスから提供してもらうのでコンポーネントではデータの代わりにサービスをインポートします。

```ts:hero.component.ts
import { Component, OnInit } from '@angular/core';
import {Hero} from '../hero';
import {HeroService} from '../hero.service';//サービスをインポート

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

  heroes: Hero[];//宣言を変更
  selectedHero: Hero;

  //サービスをインポートしたらコンストラクタで宣言する
  constructor(private heroService: HeroService) { }

  ngOnInit() {//ここで関数を呼び出す
    this.getHeroes();//下で作る関数を呼び出す
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  getHeroes(): void{//取得したデータを代入する関数を定義
    this.heroes = this.heroService.getHeroes();
  }
}
```

色々変更していますが、サービスからデータを取得してコンポーネントで使用できるようにしています。

## Observableの利用
このままではサーバーからデータを取得するとき動作しません。
そんな時でも動作するようにtsファイルを変更します。(非同期的手法)

```ts:hero.service.ts
import { Injectable } from '@angular/core';
import {Hero} from './hero';
import {HEROES} from './mock-heroes';
import {Observable, of} from 'rxjs'//ここを追加

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

  constructor() { }

  getHeroes(): Observable<Hero[]>{//ここを変更
    return of(HEROES);
  }
}
```
関数の戻り値とその型を変更しました。こうすることで、サーバーからデータを取得する際に時間がかかっても待ってくれます。ここで型を変更したので`hero.component.ts`も変更する必要があります。

```ts:hero.component.ts
  getHeroes(): void{//変更した関数
    this.heroService.getHeroes().subscribe(
      heroes => this.heroes = heroes
    );
  }
```
もう正直訳わからないですね。今はおまじない程度でいいと思います。これで今まで通り動作します。

## メッセージの表示
データを取得できたときメッセージを出力するようにします。

今回メッセージを出力させるmessageコンポーネントに加えて、「データ取得との紐付け・メッセージを記憶・コンポーネントへ渡す」の役割をはたすmessageサービスの二つを作成します。

```terminal:ターミナル
$ cd src/app
$ ng generate component message
$ ng generate service message
```

次に出力する場所をhtmlで一番下に設定します。

```html:app.component.html
<h1>{{title}}</h1>
<app-heroes></app-heroes>
<app-message></app-message>
```
サービスでメッセージを追加、消去する関数を定義します。

```ts:message.service.ts
import { Injectable } from '@angular/core';

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

  messages: string[] = [];//メッセージを代入する変数を定義

  constructor() { }

  add(message:string){//引数のmessageをmessagesにpushで代入
    this.messages.push(message);
  }

  clear(){//messagesの中身を空にする
    this.messages = [];
  }
}
```

ここで作成された関数を`hero.service.ts`で使用します。

```ts:hero.service.ts
import { Injectable } from '@angular/core';
import {Hero} from './hero';
import {HEROES} from './mock-heroes';
import {Observable, of} from 'rxjs';
import {MessageService} from './message.service';//サービスをインポート

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

  //サービスをインポートしたのでコンストラクタでサービスを宣言
  constructor(private messageService: MessageService) { }

  getHeroes(): Observable<Hero[]>{
    //データを取得したらメッセージを代入
    this.messageService.add('HeroService: fetched heroes');
    return of(HEROES);
  }
}
```

ここで代入されたmessagesをmessageコンポーネントに渡します。

```ts:message.component.ts
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';//サービスをインポート

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

  //サービスをインポートしたのでコンストラクタでサービスを宣言
  constructor(public messageService: MessageService) { }

  ngOnInit() {
  }

}
```
今回はコントラクタでサービスの宣言を`public`で行ったので、messageコンポーネント内でサービスが使用できます。それを利用してHTMLを書いていきます。

```html:message.component.html
<div *ngIf="messageService.messages.length">

    <h2>Messages</h2>
    <button class="clear"
            (click)="messageService.clear()">clear</button>
    <div *ngFor='let message of messageService.messages'> {{message}} </div>
  
</div>
```
<img width="325" alt="スクリーンショット 2020-02-04 20.51.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519379/b6a303d2-10e3-6e6a-e4a8-aaa43ed70fb7.png">
このようにメッセージが出力されるようになりました。