Angularの公式Tutorialは、全体をとおしてひとつの作例「Angular Example - Tour of Heroes: Part 6 」をつくります。この解説を下じきに、もっと細かくコードの動きが確かめられるように、説明の仕方やサンプルも改めて「Angular 5入門」を書きました。そのとき、公式の作例にいくつか気づいて直した点があります。それらをご紹介しましょう。
要らないコードを除く
「Routing」の「Remove dead code (optional)」は、書き直しの結果要らなくなったコードを除いています。これに加えてもうひとつ削った方がよいのは、HeroDetailComponentクラスのデコレータ関数Input()です。
import { Component, OnInit, Input } from '@angular/core';
export class HeroDetailComponent implements OnInit {
	@Input() hero: Hero;
}
コンポーネントの詳細情報(hero-detail.component)をリスト表示(heroes.component)から分け、選択されたデータをサービスから得るようにしたからです。親だったリスト表示のコンポーネントから参照を得るためにプロパティ(hero)に添えていたデコレータ関数Input()は意味がなくなります。
import { Component, OnInit} from '@angular/core';
export class HeroDetailComponent implements OnInit {
	hero: Hero;
}
公式Tutorialにもこの修正が加えられそうです(「refactor(aio): refining code of tutorial 7 routing」)。
リストを空にするとデータが加えられない
まず、リスト表示の画面ですべての項目を削除します(図001)。
図001■リスト項目がなくなった
つぎに、新たな項目をテキストフィールドに入力して、[add]ボタンを押します(図002)。
図002■新たな項目を入力する
すると、番号もテキストも空の項目が加えられます(図003)。ブラウザのコンソールを確かめると、つぎのようなエラーが示されました。
body: {error: "Collection 'heroes' id type is non-numeric or unknown. Can only generate numeric ids."}
図003■番号とテキストが空の項目
データの配列が空になって、プロパティidをもつオブジェクトがなくなると、新たな番号はつくれず、データも加えられないようです。
HttpClient.put()メソッドを使えば、データにid番号を定めて追加できます。リストが空のときは、つぎのようにidは初期値にしてHttpClient.put()メソッドでデータを送ればよいでしょう。
export class HeroService {
	// addHero(hero: Hero): Observable<Hero> {
	addHero(hero: Hero, numHeros: number): Observable<Hero> {
		let heroOvserbable;
		// return this.http.post<Hero>(this.herosUrl, hero, httpOptions)
		if (numHeros === 0) {
			hero.id = 11;
			heroOvserbable = this.http.put(this.herosUrl, hero, httpOptions)
		} else {
			heroOvserbable = this.http.post<Hero>(this.herosUrl, hero, httpOptions);
		}
		return heroOvserbable
	}
}
ただし、サービスのクラス(HeroService)はデータの配列を知りません。ですから、リスト表示クラス(HerosComponent)がデータ追加(add()メソッド)のためにサービス(HeroService)のメソッド(addHero())を呼び出すとき、第2引数としてデータ(heros)の長さ(Array.lengthプロパティ)を渡します。
export class HerosComponent implements OnInit {
	add(name: string): void {
		// this.heroService.addHero({name} as Hero)
		this.heroService.addHero({name} as Hero, this.heros.length)
	}
}
これで、リストを空にしても新たな項目が加えられます。これらの手直しをした作例は「Angular Example - Tour of Heroes: Part 6 revised」に掲げました。
Angular in-memory-web-apiモジュールのメソッドをオーバーライドする
リストを空にすると新たな項目がつくれないのは、Angular in-memory-web-api moduleの仕様にもとづくようです。デフォルトのid番号を返すBackendService.genIdDefault()メソッドは、BackendService.isCollectionIdNumeric()メソッドで、リストのデータがあり、さらに数値のidプロパティをもつかどうか確かめます。そうでなければ、デフォルトのid番号はできず、エラーになるのです。
データにid番号を振るのはBackendService.genId()メソッドです。このメソッドは、InMemoryDataServiceクラス(in-memory-data.service)でオーバーライドできます(「Tutorial example has a problem to enter new item into an empty list」参照)。すると、前項のようにサービス(HeroService)やリスト表示(HerosComponent)のクラスは書き替えずに済むのです。
InMemoryDataServiceクラスに定めてオーバーライドするつぎのgenId()メソッドは、データがあればid番号の最大値 + 1、なければ初期値を返します。この作例は"Angular Example - Tour of Heroes: Part 6"に掲げられています。
export class InMemoryDataService implements InMemoryDbService {
	genId(heroines: Heroine[]): number {
		return heroines.length > 0 ? Math.max(...heroines.map(heroine => heroine.id)) + 1 : 11;
	}
}
おまけ
細かいところで、もうひとつつけ加えておきます。サービスのクラス(HeroService)の検索メソッド(searchHeroes())で、URLを直書きしているところです。もとのパスはプロパティ(heroesUrl)に定めてあります。後あとの修正も考えるなら、プロパティを用いるべきでしょう。
export class HeroService {
	searchHeroes(term: string): Observable<Hero[]> {
		const url = `${this.heroesUrl}/?name=${term}`;
		// return this.http.get<Hero[]>(`api/heroes/?name=${term}`)
		return this.http.get<Hero[]>(url)
	}
}


