💻
Angularで外部のコンポーネントがレンダリングするDOMにスタイルを当てたいとき
どういうこと?
下記ツイートのとおり、
- 自分のコンポーネントで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]
はついていない(外部のコンポーネントによる出力なので)ので、マッチせずスタイルが当たらない
という現象があってちょっと悩みました。
答え
ちょっとググったらまさに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/
を使えばそのスタイルだけカプセル化を無効にできる
とのこと。なるほどです。
試してみた結果
ドキュメント のとおり
:host /deep/ .mat-figure {
// ...
}
のように書いてみたところ、僕の環境(Angular 9)ではコンパイルできませんでした🙄
シンタックスシュガーである ::ng-deep
を使って
:host ::ng-deep .mat-figure {
// ...
}
と書いてみたところ無事コンパイルできて、意図どおり [_nghost-esh-c71] .mat-figure {}
という感じのCSSに展開されてスタイルが適用できました。(関連Issue)
まとめ
- 一応
::ng-deep
を使えば部分的にカプセル化を無効にできることが分かりましたが、deprecatedな機能なので素直にViewEncapsulation.None
を使ったほうがいいかもです
Discussion