WESEEK Tech Blog

WESEEK のエンジニアブログです

Angular Tips その3

こちらは 「SEROKUを支える技術 Angular Tips編 その3」からの転載です。

記事の最後に関連記事を掲載しています。よろしければご参考にどうぞ。




さて、今回は前回予告したとおり ng-content を使った、テンプレートに対して外部から子要素を突っ込むような処理を書いてみようかと思います。

Angular に置いてあまりサンプル記事がない印象ですが、使い方さえ覚えておけばきっと役に立つはずです!

Tips 4 :ng-content を使った子要素への注入

ng-content とは、作成したコンポーネントに対して外部から要素を注入するときに使うタグです。

ng-content を使ったシンプルな例

まずは一番簡単な例を、コードベースで書いていきたいと思います。

selector: ‘foo’ とする Foo コンポーネントを定義

<div>
  <h1>header要素</h1>
  <!-- 以下ng-contentを配置し、外部から注入するための要素を宣言 -->
  <ng-content></ng-content>
</div>

外部のコンポーネントで foo を呼び出し、さらに要素を注入

    <foo>
      <div>test</div>
      <span>test2</span>
    </foo>

上記の foo コンポーネントを呼び出した結果の html

    <div>
      <h1>header要素</h1>
      <!-- angular によって展開され、foo タグに書いたタグが全て ng-content に展開される -->
      <div>test</div>
      <span>test2</span>
    </div>

foo タグ内部に書いた要素が、まるまる ng-content と置き換わりますね。 上記の簡単な例を実装する上ではうまくいきましたが、では複数の要素を別々の場所に注入したい場合はどうすればよいでしょう?

ng-content を使い複数の要素を注入する例

ng-content では select アトリビュートを使い、注入するコンテンツを指定することができます。 以下のコードで複数の要素を注入したいと思います。

selector: ‘foo’ とする Foo コンポーネントを定義

<div>
  <!-- ng-contentを宣言(selectでh1を指定) -->
  <ng-content select="h1"></ng-content>
  <!-- ng-contentを宣言(selectで.headerを指定) -->
  <ng-content select=".header"></ng-content>
  <!-- 以下ng-contentを宣言(select無し) -->
  <ng-content></ng-content>
</div>

外部のコンポーネントで foo を呼び出し、さらに要素を注入

    <foo>
      <h1>test</h1>
      <div class="header">test2</div>
      <div>test3</div>
      <span>test4</span>
    </foo>

上記の foo コンポーネントを呼び出した結果の html

    <div>
      <!-- select で h1 を指定しているので h1 タグの要素が展開される -->
      <h1>test</h1>
      <!-- select で .header を指定しているので header クラスを指定した要素が展開される -->
      <div class="header">test2</div>
      <!-- 上記指定に当てはまらないタグが全て ng-content に展開される -->
      <div>test3</div>
      <span>test4</span>
    </div>

foo タグ内部に書いた要素の中で、Foo コンポーネントの ng-content の select で指定した箇所にそれぞれ置き換わりますね。

ちなみに、上記の例で言うと h1 タグが複数あった場合、または header クラスが複数あった場合でも、select にマッチする要素分 ng-content は置き換わります。

ng-content を使った例 応用編

上記の例はあくまでただの使い方の例だったので、どういう時に役に立つかをユースケースを交えながら書いていきたいと思います。

一つの例として、コンポーネントを作る際に、あるボタンイベントを親コンポーネントに EventEmitter を使って通知し、実際の処理は親コンポーネントでやらせているケースがあるとします。

selector: ‘foo’ とする Foo コンポーネントを定義

// テンプレート側
<div>
  <button (click)="clickHandler()"></button>
</div>


// コンポーネント側
@Output()
onClicked = new EventEmitter();

clickHandler() {
    onClicked.emit();
}

外部のコンポーネントで foo を呼び出し onClicked に対してイベントハンドラを仕掛けている

// テンプレート側
<foo (onClicked)="clickHandler()" ></foo>

// コンポーネント側
clickHandler() {
    // 実際の処理
}

Foo コンポーネント側の clickHandler で、なにか特別なことをしているのなら別ですが、ただ emit しているだけならば以下のように書いたほうが、Foo コンポーネントはロジックを気にせずにただの View テンプレートとしての役割に専念できるので見通しが良いです。

selector: ‘foo’ とする Foo コンポーネントを定義

// テンプレート側
<div>
  <ng-content select="button"></ng-content>
</div>


// コンポーネント側
処理がなくなる!!

外部のコンポーネントで foo を呼び出し click ハンドラを設定している button タグを要素として注入

// テンプレート側
<foo>
    <button (click)="clickHandler()"></button>
</foo>


// コンポーネント側
clickHandler() {
    // 実際の処理
}

上記は一例に過ぎませんが、同じような状況は結構ある気がします。 これ使ったほうがコードがキレイになりそうというのがもしあれば試してみると良いかもしれません。

まとめ

今回は ng-content に関して書きました。

前回の記事で ngTemplateOutlet にも少し触れましたが、ある特定の要素を置き換えるという場合にも複数のやり方がありますね。(もちろんそれぞれで適切な使い所は異なりますが)

吐き出したいネタが一旦尽きてしまったので、なにか思いついたらまたアップしたいと思います。

それでは皆さんごきげんよう

関連記事

tech.weseek.co.jp

tech.weseek.co.jp