💻

Angularで外部のコンポーネントがレンダリングするDOMにスタイルを当てたいとき

2020/06/15に公開

どういうこと?

下記ツイートのとおり、

  • 自分のコンポーネントでAngular Materialを利用していて
  • 自分のコンポーネントに <mat-grid-tile></mat-grid-tile> とか書くと、実際のレンダリング結果では <mat-grid-tile></mat-grid-tile> の内側にAngular Materialによって .mat-figure なDOMが出力される
  • この .mat-figure にスタイルを当てたかったけど、普通に自分のコンポーネントのスタイルファイルに mat-grid-tile .mat-figure {} とか書いてもダメだった
  • レンダリングされたCSSを見ると、 mat-grid-tile[_ngcontent-gix-c71] .mat-figure[_ngcontent-gix-c71] {} のように .mat-figure にも カプセル化 用の識別子が付いていて、DOMのほうには当然 .mat-figure[_ngcontent-gix-c71] はついていない(外部のコンポーネントによる出力なので)ので、マッチせずスタイルが当たらない

という現象があってちょっと悩みました。

https://twitter.com/ttskch/status/1268474510451208192

https://twitter.com/ttskch/status/1268474839121096704

答え

ちょっとググったらまさにAngular Materialのドキュメントに答えが書いてありました。

If your component has view encapsulation turned on (default), your component styles will only affect the top level children in your template. HTML elements belonging to child components cannot be targeted by your component styles unless you do one of the following:

  • Add the overriding style to your global stylesheet. Scope the selectors so that it only affects the specific elements you need it to.
  • Turn view encapsulation off on your component. If you do this, be sure to scope your styles appropriately, or else you may end up incidentally targeting other components elswhere in your application.
  • Use a deprecated shadow-piercing descendant combinator to force styles to apply to all the child elements. Read more about this deprecated solution in the Angular documentation.

https://material.angular.io/guide/customizing-component-styles#styling-other-components

  • グローバルスタイルに書くか、
  • 自分のコンポーネントのカプセル化を無効にするか、
  • deprecatedだけど /deep/ を使えばそのスタイルだけカプセル化を無効にできる

とのこと。なるほどです。

https://twitter.com/ttskch/status/1268478682936442880

試してみた結果

ドキュメント のとおり

:host /deep/ .mat-figure {
  // ...
}

のように書いてみたところ、僕の環境(Angular 9)ではコンパイルできませんでした🙄

シンタックスシュガーである ::ng-deep を使って

:host ::ng-deep .mat-figure {
  // ...
}

と書いてみたところ無事コンパイルできて、意図どおり [_nghost-esh-c71] .mat-figure {} という感じのCSSに展開されてスタイルが適用できました。(関連Issue

https://twitter.com/ttskch/status/1268479318285582336

https://twitter.com/ttskch/status/1268479555007741953

まとめ

  • 一応 ::ng-deep を使えば部分的にカプセル化を無効にできることが分かりましたが、deprecatedな機能なので素直に ViewEncapsulation.None を使ったほうがいいかもです
GitHubで編集を提案

Discussion