← 解説「*ngIf と *ngFor — @if / @for 以前の制御フロー」に戻る
サンプルコードで身につける: *ngIf と *ngFor — @if / @for 以前の制御フロー
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1新旧対訳表 — 制御フロー一覧
3つの制御構文の新旧をまとめて対比します。まずこの表を眺めてから、以降のサンプルで細部を見ていくと頭に入りやすいです。
<!-- 条件分岐 -->
<!-- 旧 --> <p *ngIf="ok">OK</p>
<!-- 新 --> <!-- @if (ok) { <p>OK</p> } -->
<!-- 繰り返し -->
<!-- 旧 --> <li *ngFor="let x of list; trackBy: byId">{{ x.name }}</li>
<!-- 新 --> <!-- @for (x of list; track x.id) { <li>{{ x.name }}</li> } -->
<!-- 多分岐 -->
<!-- 旧 -->
<div [ngSwitch]="status">
<p *ngSwitchCase="'ok'">正常</p>
<p *ngSwitchDefault>不明</p>
</div>
<!-- 新 --> <!-- @switch (status) { @case ('ok') { ... } @default { ... } } -->
2else 分岐の読み方 — ng-template 参照
旧記法のelseは、@elseのようなブロックではなく「別の場所に置いた下書きを名前で参照する」形です。テンプレート内で#の名前を探して対応を突き合わせる読み方になります。
<!-- 旧: else は #名前 で下書きを参照する -->
<div *ngIf="items.length > 0; else empty">
<li *ngFor="let item of items">{{ item }}</li>
</div>
<!-- 対応する下書きは離れた場所にあることも多い -->
<ng-template #empty>
<p>データがありません</p>
</ng-template>
<!-- then/else 両方指定という形もあった -->
<div *ngIf="loaded; then content; else spinner"></div>
<ng-template #content>本体</ng-template>
<ng-template #spinner>読み込み中...</ng-template>
3trackBy は関数をクラス側に書く
@forのtrackは式を直接書けますが、旧記法のtrackByは「(index, item)を受け取る関数」をコンポーネントに定義して名前で渡します。関数の第1引数がindexである点も間違えやすいポイントです。
@Component({
selector: 'app-user-list',
template:
'<li *ngFor="let user of users; trackBy: byId">' +
' {{ user.name }}' +
'</li>',
})
export class UserListComponent {
users = [{ id: 1, name: '松本' }];
// 旧: trackBy 用の関数。第1引数は index、第2引数が要素
byId(index: number, user: { id: number }) {
return user.id;
}
}
// 新記法なら関数は不要で、式を直接書ける:
// @for (user of users; track user.id) { ... }
4async as — 購読と分岐の合わせ技イディオム
旧コードで最も出会う頻度が高いイディオムです。asyncパイプの結果に名前を付け、nullの間は表示しない、という3つの仕事を1行でやっています。分解して読めるようにしておきましょう。
<!-- この1行がやっていること:
1. user$ を購読する(asyncパイプ)
2. 届いた値に user と名前を付ける(as)
3. まだ null の間は else 側を出す(*ngIf) -->
<div *ngIf="user$ | async as user; else loading">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
<ng-template #loading><p>読み込み中...</p></ng-template>
<!-- 現代版の対訳 -->
<!-- @if (user$ | async; as user) {
<h2>{{ user.name }}</h2>
} @else {
<p>読み込み中...</p>
} -->
5自動移行コマンドで一括変換する
既存プロジェクトを@構文へ移行するときは、手作業ではなく公式のスキーマティックを使います。変換の仕組みを知っていれば、移行直後のコードレビューで差分を安心して読めます。
# *ngIf / *ngFor / *ngSwitch を @if / @for / @switch へ一括変換
ng generate @angular/core:control-flow
# 対話で対象ディレクトリを聞かれる。変換内容の例:
# *ngIf="cond; else tpl" → @if (cond) { } @else { }
# (参照されていた ng-template は中身が @else へ移動)
# *ngFor + trackBy 関数 → @for + track 式
#
# 変換後にやること:
# 1. ng build で型エラーが無いか確認
# 2. 使われなくなった CommonModule の imports を掃除
# 3. 差分をPRにして、画面の見た目が変わっていないか確認