Angular 4.0にはいくつかの新しい機能が追加されます。 今回はngIfに追加される新しい機能について解説します。
ngIfの新機能
ngIfThen: テンプレート分離
これまでのngIfで表示を切り替えられるのは、ngIfディレクティブが付与された要素とその内側の要素だけでした。 Angular 4.0以降は、ngIfによる条件付けと、その条件により制御されるテンプレートを分離できます。 次の例では、showプロパティが真のときに#thenBlockのテンプレートが描画されます。
<div *ngIf="show; then thenBlock">ignored</div>
<ng-template #thenBlock>show === true</ng-template>
thenテンプレートにはTemplateRefのインスタンスを渡せます。nullのときは無視されます。 次の例では、条件によってngIfによって描画されるテンプレートを切り替えています。 それほどユースケースは多くないですが、必要になる場面があるかもしれません。
@Component({
selector: 'ng-if-then',
template: `
<button (click)="switchPrimary()">Switch Template</button>
<div *ngIf="show; then thenBlock"></div>
<ng-template #primaryBlock>Primary</ng-template>
<ng-template #secondaryBlock>Secondary</ng-template>
`
})
class NgIfThenElse implements OnInit {
thenBlock: TemplateRef<any> = null;
show: boolean = true;
@ViewChild('primaryBlock')
primaryBlock: TemplateRef<any> = null;
@ViewChild('secondaryBlock')
secondaryBlock: TemplateRef<any> = null;
switchPrimary() {
this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock;
}
ngOnInit() { this.thenBlock = this.primaryBlock; }
}
ngIfElse: 偽のときのテンプレート
先ほどのthenテンプレートはこのelseテンプレートの副産物とも言えるでしょう。 その名前のとおり、ngIfに渡された式が偽のときに描画されるテンプレートを指定する仕組みです。 elseテンプレートは次のように使います。
<div *ngIf="show; else elseBlock">show === true</div>
<ng-template #elseBlock>show === false</ng-template>
thenテンプレートとelseテンプレートは併用できます。
<div *ngIf="show; then thenBlock; else elseBlock"></div>
<ng-template #thenBlock>show === true</ng-template>
<ng-template #elseBlock>show === false</ng-template>
これまでは真の場合と偽の場合にそれぞれ逆の条件のngIfが必要でしたが、簡単に書けるようになります。
As構文: 評価結果の変数化
これこそがngIf最大の変更点です。 ngIfに渡された式の評価結果をローカル変数にアサインできるようになりました。 これはasyncパイプとngIfを併用するケースで絶大な効果を発揮します。 次の例を見てみましょう。 非同期にユーザー情報が得られるコンポーネントで、データ取得後に.nameプロパティを表示したいとき、 これまではこのようにasyncパイプと?.構文を組み合わせていました。
<p>{{ (user$ | async)?.name }}</p>
これが.name以外にも.ageや.iconなども使いたいとなると、テンプレートは大変なことになります。
<p>{{ (user$ | async)?.name }}</p>
<p>{{ (user$ | async)?.age }}</p>
<img [src]="(user$ | async)?.icon" />
せめて?.をなくそうとngIfで囲っても、asyncパイプは全てのバインディングに必要です。
<div *ngIf="user$ | async">
<p>{{ (user$ | async).name }}</p>
<p>{{ (user$ | async).age }}</p>
<img [src]="(user$ | async).icon" />
</div>
Angular 4.0以降は、asを使うことで評価結果を変数として保持できます。つまり、user$ | asyncの結果であるユーザーデータを同期的に扱えるようになります。具体的には、次のように書けます。
<div *ngIf="user$ | async as user">
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<img [src]="user.icon" />
</div>
Woohoo!!!!! 🎉🎉🎉🎉🎉
この変化は単にテンプレートがきれいになるだけでなく、asyncパイプが減ることによるパフォーマンスの改善も得られます。 アプリケーションをObservableベースのリアクティブな設計したときも、テンプレートが自然に書けるようになります。
elseテンプレートと併用すれば、今まではとても複雑になっていたテンプレートが次のようにスッキリします。
<div *ngIf="user$ | async as user; else userNotFound">
<p>{{ user.name }}</p>
<p>{{ user.age }}</p>
<img [src]="user.icon" />
</div>
<ng-template #userNotFound>
<p>not found</p>
</ng-template>
このas構文はngForでも使用できます。
<div *ngFor="let user of (users$ | async) as users; index as i">
<span>{{ i + 1 }} / users.length</span>
<span>{{ user.name }}</span>
</div>
まとめ
thenテンプレートでテンプレートの分離と切り替えが可能になるelseテンプレートで偽のときのテンプレートを指定できるasによる変数化でasyncパイプとの親和性が改善される
Angular 4.0 Features