Skip to content

Instantly share code, notes, and snippets.

@ver-1000000
Last active September 30, 2022 01:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ver-1000000/3d03a3e3d2c269cb5c450116c8e11407 to your computer and use it in GitHub Desktop.
Save ver-1000000/3d03a3e3d2c269cb5c450116c8e11407 to your computer and use it in GitHub Desktop.
AngularのTrackByFunctionをカンタンに作るパイプ
import { Pipe, PipeTransform } from '@angular/core';
/**
* `{ id: number, name: string }[]`のような、ユニークな値を持つオブジェクトの配列に対して、
* 簡単に{@link https://angular.io/api/core/TrackByFunction TrackByFunction}を設定するためのパイプ。
*
* @example
* <!-- `{ id: number, name: string }[]` -->
* <div *ngFor="let item of items; trackBy: 'id' | keyTrackBy">{{ item.id }}: {{ item.name }}</div>
*
* <!-- `{ id: number | null, name: string | null }[]` -->
* <input name="name" [(ngModel)]="item.name" *ngFor="let item of items; trackBy: null | keyTrackBy">
*/
@Pipe({ name: 'keyTrackBy' })
export class KeyTrackByPipe implements PipeTransform {
transform<T>(key: null | keyof T): (_: number, item: T) => T | T[keyof T] {
return (_: number, item: T) => key == null ? item : item[key];
}
}
@studioTeaTwo
Copy link

studioTeaTwo commented Jan 31, 2020

これってtemplate側で型チェック効きますか?

TrackByFunction のリターンはitem[key] || item; でもいいかもしれないと思いました。
エラーを握り潰すみたいな話になるかもしれませんが。

@ver-1000000
Copy link
Author

@studioTeaTwo

これってtemplate側で型チェック効きますか?

Angular 8.2.11で、以下のようなコードを用いて確認してみました。

  <!-- items = [{ id: 1, name: 'a' }, { id: 2, name: 'a' }, { id: 3, name: 'a' }, { id: 4, name: 'a' }]; -->
  {{ ('id' | keyTrackBy)(1, items[0]) }}

keyTrackByの引数('id')はvscode(Angular Language Service/Javascript and TypeScript Nightly導入)とvim(ALE導入)では補完されず、また、'id'ではなくnull1を入れても型警告を出しませんでした。

しかし、引数にtypeof Itemではない値('hoge')を入れてaotビルドしてみたところ、型エラー出してくれました。

Ivyになったらよさそうですね。

TrackByFunction のリターンはitem[key] || item; でもいいかもしれないと思いました。
エラーを握り潰すみたいな話になるかもしれませんが。

これは私も思ったのですが、使うときのことを考えると「自身返すようにしたい時、nullいれんの?空文字いれんの?undefinedいれんの?falseいれんの?」みたいな話が出てきて、そういう揺れがプロジェクト内にはびこるのも嫌だなぁ、と。。。(Prettierとかで制限できんのかな?テンプレートは無理か)(keyがnullのオブジェクトとかも作れっちゃ作れるからそこらへん考慮するとやはり設計に気持ち悪さが残る)

あとはまぁこれしないほうが型が厳密っていう話もありますね。

でも…… TeaTwoさんがおっしゃるならこの実装もアリかな〜〜って気持ちになってきたのでのちのちこうするかも。 やっぱり毎回trackByFn書くの嫌すぎなんで……。

@ver-1000000
Copy link
Author

ver-1000000 commented Jan 31, 2020

ちょっと書きなおしてみたんですけど割といいかもしれない!?

そもそもキーにnull設定とかしねぇだろがよォって話ですしこうしようかな!!!

@studioTeaTwo
Copy link

trackByに関数参照で渡すため、ジェネリクス指定できない状態なので難しそうだなと思ってました。

someArray.map(someFunction<Hoge>) ←できない

パイプを実行記述して、かつ引数itemをngForでイテレータされたitemじゃなくて、元のコレクションの添字指定にすると、AoTで検出できるんですね。
ngForでイテレータされたitemだと検出できないのは、バグっぽいですね...。

@ver-1000000
Copy link
Author

ts、型推論がいい感じにやってくれるのでいいですね〜。

ngForでイテレータされたitemだと検出できないのは、バグっぽいですね...。

これはどういうことだろう、できるのでは……? ngForの中で使っても同じかと思いますよ。(↑の確認コード、ngFor内で行わなかったのは、わかりにくいかなぁ〜と思ってのことでした。 逆にわかりにくくなってしまったのかな!?)

@studioTeaTwo
Copy link

studioTeaTwo commented Feb 1, 2020

手元でやっているとそういう結果になるんですけど、AoTで検出できてます?
*ngFor ofはショートカットで内部的にはAngularがごにょごにょ展開してるから難しいのかなあと勝手に忖度してました!

@ver-1000000
Copy link
Author

@studioTeaTwo

ホワっ!? 確認してみたらホントですね……!! そしてなぜteatwoさんが「〜添字指定にすると〜」と敢えて言っていたのか理解した…… 関数の引数にitemを渡すことを明示的にしないと〜という話だったんですね。

実装はここかな? 確かに、ここのTの型でItem型ってのはわかってそうだしなんか型読み取れそうなのでバグっぽいのかも……。 展開コードまでみないとダメな理由わからなさそうだな……。

@studioTeaTwo
Copy link

ありがとうございます。条件・環境の何かの違いでできる場合あるのかな?と気になってたので、スッキリしました!

展開コードまでみないとダメな理由わからなさそうだな……。

ngcの出力結果でわかりそうですが、追うの辛いw

@ver-1000000
Copy link
Author

ver-1000000 commented Aug 22, 2022

transformの返り値としてTrackByFunctionみたいなものを返すようにしていたけど、どうしてもうまくいかないので関数を返す型をそのまま書いた……。 これによって、存在しないキーを指定したりできないようになったと思う。

型マスターが助言してくれたら嬉しい……。


ちなみに、↑のteatwoさんとのやり取りの問題はいつの間にか解消していたようです? 普通に推論効いてるっぽいです

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment