サービス
サービスとは、データを取得しコンポーネントに提供する部分です。
サービスの作成
$ cd src/app
$ ng generate service hero
hero.service.ts
とhero.service.spec.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コンポーネントでデータをインポートしていましたが、サービスから提供してもらうのでコンポーネントではデータの代わりにサービスをインポートします。
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ファイルを変更します。(非同期的手法)
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
も変更する必要があります。
getHeroes(): void{//変更した関数
this.heroService.getHeroes().subscribe(
heroes => this.heroes = heroes
);
}
もう正直訳わからないですね。今はおまじない程度でいいと思います。これで今まで通り動作します。
メッセージの表示
データを取得できたときメッセージを出力するようにします。
今回メッセージを出力させるmessageコンポーネントに加えて、「データ取得との紐付け・メッセージを記憶・コンポーネントへ渡す」の役割をはたすmessageサービスの二つを作成します。
$ cd src/app
$ ng generate component message
$ ng generate service message
次に出力する場所をhtmlで一番下に設定します。
<h1>{{title}}</h1>
<app-heroes></app-heroes>
<app-message></app-message>
サービスでメッセージを追加、消去する関数を定義します。
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
で使用します。
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コンポーネントに渡します。
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を書いていきます。
<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>
