3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Angular】同じコンポーネント内に同じ属性の<ng-content>は複数書けない話

Posted at

概要

Angularの<ng-content>便利ですよね。親コンポーネントから子コンポーネントにhtmlを渡すのによく使います。
今回はそんな<ng-content>を同じコンポーネント内で複数設置したとき、適切にレンダリングされなかったお話です。
どんなコードで起こり、具体的にどんな問題なのか、そしてそれをどうやって解消したか紹介します。

前提

この記事はAngular 9.1.0を前提としています。今後のバージョンアップ次第で動作が変わる可能性があります。

どんなコードを書いていたか

下記のようなコードです。

<span *ngIf="flg">
  <ng-content></ng-content>
</span>
<ng-container *ngIf="!flg">
  <ng-content></ng-content>
</ng-container>

何かしらの変数(ここではflg)によって表示するhtmlが異なる、ということをしたかったわけです。今回の場合、flgがtrueの場合は<span>タグ内に親コンポーネントから渡されたhtmlを表示したい、flgがfalseの場合は親コンポーネントから渡されたhtmlをそのまま表示したいというケースです。そのため、<ng-content>が2箇所で必要でした。

しかしこれを実際に動かしてみるとflgがtrueの場合は親コンポーネントから渡ってきたhtmlが適切にレンダリングされますが、flgがfalseの場合はレンダリングされないという現象に陥りました。

なぜ適切にレンダリングされないのか

下記のIssueに書いてありましたが、どうやら<ng-content>はコンポーネントテンプレート内で必ず1つにしなければならないように設計されているようです。
Strange behaviour with multiple and *ngIf · Issue #22972 · angular/angular

ここで言う必ず1つというのは 同じ属性が付与された<ng-content> が必ず1つである必要があるということです。<ng-content>はselect属性を設定可能ですが、複数の<ng-content>で別々のselect属性が設定されていれば異なる<ng-content>と判別されます。逆にどれもselect属性が同じであったり、どれも全く設定されていない場合は同じ<ng-content>になるため、上記のような適切にレンダリングされないということが起こります。

じゃあどうすればいいのか

解消策として、同じ属性の<ng-content>を複数書かないようにすればうまくいきます。具体的には<ng-template>を使って<ng-content>の部分を一つに括りだし、それを参照するようにすれば解消できます。
具体的なコードは以下のようになります。

<ng-template #content>
  <ng-content></ng-content>
</ng-template>
<span *ngIf="flg">
  <ng-container *ngTemplateOutlet="content"></ng-container>
</span>
<ng-container *ngIf="!flg">
  <ng-container *ngTemplateOutlet="content"></ng-container>
</ng-container>

このようにすれば同じ属性の<ng-content>が複数個存在することがなくなるので、flgがtrueの場合もfalseの場合も適切にレンダリングされます。

最後に

ということで同じコンポーネント内の同じ属性の<ng-content>の複数設置回避方法を紹介しました。ただ先程のIssueを見ていると、この件についてもう少し柔軟にできるよう設計を変更するようなPRが2018/05時点ですでにいくつかあるようです。ただしIvy(Angular9でデフォルトとなったレンダリングエンジン)が利用可能になってからそれを目指していると記載があります。
2020/05現在、Ivyはデフォルトのレンダリングエンジンとなりましたし、<ng-content>周りの設計が変わりもう少し柔軟に使えるようになる日も近いかもしれません。

参考

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?