Dev Study
← Angular コース

71. ng-template と ng-container — 構造ディレクティブの正体

前のレッスンで出てきた <ng-template> の正体を掘り下げます。ng-template は「描画されない下書き」です。テンプレートに書いても、その中身はそのままではDOMに出力されません。誰かが「この下書きを実体化して」と要求したときに初めて描画されます。*ngIf の else で #loading という名前の下書きを参照できたのはこの仕組みのおかげです。実は @if や @defer が内部でやっていることも本質は同じで、「ブロックの中身を下書きとして持ち、条件が満たされたら実体化する」という機構です。

そして * 記法の正体は、この下書きを作るための省略記法(糖衣構文)です。*ngIf="cond" と書くと、Angularはその要素を <ng-template [ngIf]="cond"> で包んだ形に展開します。中身を下書き化して、条件に応じて実体化・破棄する — DOMの構造そのものを変えるので「構造ディレクティブ」と呼ばれ、1つの要素に * 付きは1つしか書けない、というお馴染みの制約もこの展開ルールから来ています。

もう1つの脇役が <ng-container> で、こちらは「DOMにタグを出さないグループ化の箱」です。複数の要素にまとめて *ngIf を掛けたいとき、div で包むとCSSレイアウトが崩れる場面で多用されました。@ ブロックで囲める現在は出番が減りましたが、既存テンプレートには大量に残っています。また、下書きを任意の場所に差し込む ngTemplateOutlet というディレクティブもあり、「テーブルの各行の描画方法を利用側が差し込む」ような柔軟な部品でいまも現役です。

サンプルコード(フレームワーク環境が必要なため表示のみ)

<!-- *ngIf は ng-template の省略記法。この2つは同じ意味 -->
<p *ngIf="visible">こんにちは</p>

<ng-template [ngIf]="visible">
  <p>こんにちは</p>
</ng-template>

<!-- ng-container: タグを出さずにまとめて条件を掛ける -->
<ng-container *ngIf="loggedIn">
  <a>マイページ</a>
  <a>ログアウト</a>
</ng-container>
<!-- → DOMには <a> 2つだけが出る(余計な箱が無い) -->

<!-- ngTemplateOutlet: 下書きを名前で差し込む -->
<ng-template #rowTemplate let-item>
  <strong>{{ item }}</strong>
</ng-template>
<ng-container *ngTemplateOutlet="rowTemplate; context: { $implicit: 'りんご' }" />
サンプルコードで身につける(5本) →

学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。

公式ドキュメントで詳しく ↗