今日も、@ngrx/data の続きです。
前回は @ngrx/data が何なのかをくどくど説明して、プロジェクトをセットアップして、ライブラリをブチ込んだところで時間切れでした。
今日はいよいよ本格的に @ngrx/data に挑戦します。
前回の話
tercel-tech.hatenablog.com
例題
| 項目名 | 型 |
|---|---|
| id | number |
| name | string |
エンティティクラスを作ろう
それじゃ、Hero エンティティのための Hero クラスを作ろう。
ちなみにエンティティ名は、今回の例に限らず基本的に名詞の単数形にしておくと吉だよ。
$ ng g class Hero --skipTests
--skipTests って何?余計な spec.ts が作られなくなるおまじない。
単なるデータの容れ物は、単体テストを書く意味があまりないからね。
src/app/hero.ts
export class Hero { id: number; name: string; }
エンティティメタデータの編集
src/app/entity-metadata.ts を開いて、以下のように編集しましょう。
import { EntityMetadataMap, EntityDataModuleConfig } from '@ngrx/data';
const entityMetadata: EntityMetadataMap = {
+ Hero: { } // ❶
};
const pluralNames = {
+ Hero: 'Heroes' // ❷
};
export const entityConfig: EntityDataModuleConfig = {
entityMetadata,
pluralNames
};
❶ を書くことで、先ほど作った Hero クラスが @ngrx/data に登録されるよ。
とりあえず、 { } の中は何も書かなくていい。
{ } の中身が必要なの?レコードを絞ったり(WHEREみたいなやつ)、ソートしたり(ORDER BYみたいなやつ)。
あと、主キーの項目名が id 以外の場合は、ここで主キーを指定する必要があるよ。
Hero の複数形を指定しているんだね。補足: エンティティの主キーの話
デフォルトでは、暗黙的に id という名の項目がエンティティの主キーと見なされるんだよ。
だから、今回の例では ❶ には特になにも指定していない。
id 以外の項目も主キーにできるんだよね?うん。 でも複合主キーは基本的に許されない。
たとえば、多対多の関係性を取り持つ連関エンティティの場合も、ちゃんと単独で機能する主キーが別途必要になるんだ。
補足: エンティティ名の単数形と複数形
❷ の plural は、複数形という意味だよ。
@ngrx/data は、API 設計的に単数形と複数形を区別しているんだ。
id = 1 みたいに、主キーを指定して単一のレコードを削除しようとしたときは、 /api/hero/1 という URL に HTTP Delete を投げる。でもタネを明かすと、 @ngrx/data がやっていることは、機械的にエンティティ名のうしろに〝s〟をつけてるだけなんだ。
たとえば Book というエンティティ名の複数形は Books みたいにね。
単純にエンティティ名のうしろに〝s〟をつけるだけではダメな場合は、@ngrx/data に教えてやる必要があるんだね。
Hero と Heroes とか、Octopus と Octopi とか。
サービス経由でエンティティを操る
Hero エンティティを操作する方法を見ていこう。src/app/app.component.ts
import { Component } from '@angular/core'; import { EntityCollectionService, EntityServices } from '@ngrx/data'; import { Hero } from './hero'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { private heroService: EntityCollectionService<Hero>; constructor(entityServices: EntityServices) { // ❶ this.heroService = entityServices.getEntityCollectionService<Hero>('Hero'); // ❷ } }
Hero をすべて Villain に換えればいいんだね。そういうこと。
❷ で取得したサービスは heroService に代入している。
heroService を通して CRUD 操作を行えばいいの?うん。
代表的なメソッドを以下にまとめたよ。
| METHOD | MEANING | HTTP METHOD WITH ENDPOINT |
|---|---|---|
| add(entity: T) | 新しいエンティティを追加します | POST /api/hero/ |
| delete(id: any) | 主キーの値でエンティティを削除します | DELETE /api/hero/5 |
| getAll() | このエンティティタイプのすべてのインスタンスを取得します | GET /api/heroes/ |
| getById(id: any) | 主キーでエンティティを取得します | GET /api/hero/5 |
| getWithQuery(queryParams: QueryParams | string) | クエリを満たすエンティティを取得します | GET /api/heroes/?name=bombasto |
| update(update: Update |
既存のエンティティを更新します | PUT /api/hero/5 |
this.heroService.getAll() を実行すると、自動的に /api/heroes/ に向けて HTTP Get が飛ぶよ。ちなみに、さっきの単数形と複数形の話を思い出して。
API によって URL が単数形だったり複数形だったりするでしょ。
ちなみにこの URL って変えられないの?
たとえば、必ずしも /api 始まりじゃないかも知れないし、もっと言うとドメインすら違うかも知れない。
コード量は少ないけど、一つひとつが意味深調だからどうしてもくどくなっちゃうんだよね。
次回は、サーバサイド処理をエミュレートする Angular In Memory Web API について集中的に取り上げていくよ。
Angular In Memory Web API 編につづく。