とんかつ時々あんどーなつ

〜たとえ低空でも飛行していられるように〜

Angular のテンプレートを同一コンポーネント内で分割するときはフラグメントが便利

はじめに

Angular のテンプレートは @if@switch といった制御フローブロックがサポートされているため、条件に応じた表示の切り替えが簡単に実現できる。

一方で、これらは多用するとテンプレートファイルの肥大化にもつながる。 このようなとき component を分割する方法が真っ先に上げられるが、component を分割しないまま、テンプレートフラグメントによる可読性の向上も選択肢として有用なので紹介する。

テンプレートフラグメント

テンプレートフラグメントは <ng-template> 要素を使って動的に表示できるコンテンツを指す。

<p>Hello</p>

<ng-template>
  <p>My name is Taro</p>
</ng-template>

上記のコードをこのままレンダリングしても ng-template の要素は表示されない。この要素を表示する方法は何パターンかあるが、この記事ではテンプレート参照変数を用いた方法を用いる。 テンプレート参照変数とは # で始まる変数のことである。これを属性として指定することで同じテンプレートから参照できるようになる。

テンプレート参照変数の宣言の仕方は次のとおりである。

<p>Hello</p>

<ng-template #myFragment>
  <p>My name is Taro</p>
</ng-template>

次にテンプレートフラグメントを参照し、レンダリングする方法として NgTemplateOutlet ディレクティブを使う方法を紹介する。

<p>Hello</p>
<ng-container [ngTemplateOutlet]="myFragment"></ng-container>

<ng-template #myFragment>
  <p>My name is Taro</p>
</ng-template>

これだけである。

テンプレートフラグメントに値を渡すこともできる。必要な場合は次のように ngTemplateOutletContext を使うことで実現できる。

<p>Hello</p>
<ng-container
  [ngTemplateOutlet]="myFragment"
  [ngTemplateOutletContext]="{ name: 'Hanako' }"
></ng-container>

<ng-template #myFragment let-name="name">
  <p>My name is {{ name }}</p>
</ng-template>

最初に書いたように制御フローでネストしそうなところをフラグメントとして切り出して <ng-container> が並ぶようにするだけでも可読性の向上に寄与するのではないだろうか?

おわりに

テンプレートフラグメントを使うことで、同一ファイル内でも擬似的にテンプレートを分割して可読性を上げる方法を取り上げた。 ngTemplateOutletContext を使った値の受け渡しも紹介したが、実はこのときの型が any になってしまうという欠点もある。

このあたり、よりベターな方法を知っている人がいたらぜひ教えて欲しい。