Goalist Developers Blog

Angular@4.1.2のCSSカプセル化について

こんにちは、ゴーリスト開発のモリツグです。

最近Angular向けのWijmo5をためしているのですが、Wijmo5の中の人はFlex愛好家だと思います。
全国に30人はいるであろうFlex愛好家が海外にも居たんだと勝手に思っています。
ブログ投稿のたびに書いてますが、Angularは悲しみに暮れるFlex愛好家にはピッタリなのでお勧めです。

今回はAngularのCSSカプセル化について書いてみたいと思います。
とはいえ、すでに先達の方々がこちらのリンクなどで詳しく説明されているので、styleUrlsまわりに絞って紹介したいと思います。
デモソースのplunkerはこちらです。

styleUrls

とにかくCSSをカプセル化したい!という場合はcssを用意してstyleUrlsで指定します。

@Component({
  selector: 'my-app',
  styleUrls : [
    `./my-app.css`,
  ],
  // encapsulation:ViewEncapsulation.Emulated
  template: `
    <div>demo</div>
    <child-comp></child-comp>
    <child-comp class="test-red"></child-comp>
  `
})

コメントアウトされているencapsulationについて説明します。
encapsulationに設定できる値はViewEncapsulation.None/Emulated/Nativeの3種類があります。
・None: カプセル化を行わない。
・Emulated: カプセル化を行う。
・Native: Shadow DOMを使う。人類にはまだちょっと早い。

encapsulationのデフォルト値はstyleUrlsの有無によって以下のように変わります。
・styleUrlsを定義しない場合: None
・styleUrlsを定義した場合: Emulated

したがって、先の例ではstyleUrlsが定義されているので、encapsulation:ViewEncapsulation.Emulatedをコメントアウトしてもしなくても同じです。

CSSカプセル化の実現方法

先ほどのコンポーネントがhtmlになった状態を見ると、以下のように<head>の中に<style>でmy-app.cssが埋め込まれており、_nghost-*や_ngcontent-*が各要素と.titleに付与されています。

<head>
 ...
  <style>
    @charset "UTF-8";
  
    .title[_ngcontent-c0] {
      color: red;
    }
  </style>
  ...
</head>
...
<my-app _nghost-c0="" ng-version="4.1.2">
  <div _ngcontent-c0="" class="title">demo</div>
  <child-comp _ngcontent-c0="" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
  <child-comp _ngcontent-c0="" class="test-red" _nghost-c1="">
    <div _ngcontent-c1="">Child Component</div>
  </child-comp>
</my-app>

Angularではこのように<style>を埋め込んでcssセレクタで_nghost-*や_ngcontent-*が一致する要素にだけcssが反映されてカプセル化を実現しているようです。

呼び出したコンポーネントの見た目を場合によって変えたい

今回の場合だと<child-comp class="test-red">のtest-redが効くようにしたい場合があると思います。
方法としては以下があります。
①:host-context疑似セレクタを使う。(今回は説明しません)
②:host疑似セレクタを使う。
③::ng-deep(以前は/deep/)セレクタを使う。(Shadow-Piercing自体があらゆるブラウザで無かったことにされるようなので使わないほうが良さそう、Angularのこの件についての公式説明はこちら)

①か②の方法が良さそうですが、今回は②の方法を紹介したいと思います。
なんか感覚的には呼び出し元のmy-app.cssで以下のように書けばcssが効きそうですが、効きません。

 /* my-app.css */
.test-red div {
  color: red;
}

正しくは<child-comp>のcssで以下のように指定します。

 /* child-comp.css */
:host(.test-red) div {
  color: red;
}

先ほど_nghost-*や_ngcontent-*という要素が挿入されていましたが、:hostは自分自身の_nghost-*に対応します。
詳しくはこちらのplunkerで確認してください。

まとめ

今回はAngularのCSSカプセル化についてザックリと紹介しました。
冒頭のQiitaのリンクが詳しくて参考になります。
本当は③の方法が一番綺麗な気がしますが、①か②の方が安全かなぁと思います。