在 Angular component 中的樣式, 基本上會被 Shadow dom 所隔離, 在 Shadow dom 之中才會是 template 的內容.
樣式中的 host 其實就是指向 Shadow dom, 也就是在父層 template 中所呼叫的 component tag.
一般會將樣式寫在 template 上而不會對 host 進行太多的定義.
Shadow dom 的大小基本上是由其中的 template 所決定, 但是如果要對這個 component 有較精準的定位或相對位置計算等空間排版的需求時, 會需要將 host 定義 display 屬性
1 | :host { |
只針對 template 要達到精準的排版是不太夠的, 要先將其 host 設定好才不會遇到太多鬼打牆的問題, 例如明明設定了但是沒用的狀況…
如果是樣式排版需要依照上層來做到自動變化, 可以透過 :host-context 來達到目的, 而不是由父層強制覆寫 component 樣式.
如果是單純調整顏色等已定義好的屬性值, 可以將這些屬性值改由 css variable 的方式來傳遞變化, 而不是濫用 :host-context 來覆寫本身的樣式
這是由 component 往上查找 theme=”theme-*“ 的用法
1 | :host-context([theme="theme-red"]) { |
:host-context 可以由這個 component 往上查找
綁定 component attribute 來達到自動寫入 host 屬性.
1 | type Theme = "theme-default" | "theme-red" | "theme-green" | "theme-blue"; |
1 | <!-- 依照 bg 這個屬性自動綁入 host 的 theme 屬性 --> |
或是利用 method 來做到複雜的判斷來寫入 host 屬性
1 | 'class.highlighted') ( |
1 | <!-- 依照 highlighted 這個函式自動綁入 host 的 class --> |
CSS Variable
在父層定義好各種不同類型的 variable, 就可以由上而下的往各 component 傳遞
1 | :host([theme="theme-default"]) { |
這是 styles.scss
1 | h1 { |
compile 後
1 | h1 { |
因為 styles.scss 是一個全域的樣式檔, 所以不會套用到 shadow dom 的樣式封裝
即使加了 ::ng-deep 穿透封裝, 也不會有任何作用, 單純的 compile 出來而已
這是 app-component.scss
1 | .app-component { |
compile 後
1 | .app-component[_ngcontent-tpg-c0] h1[_ngcontent-tpg-c0] { |
如果是 component style 的話就會被 shadow dom 封裝樣式, 會看到 [_ngcontent-xxx-xxx 這種自動產生的名稱
使用 ::ng-deep 目的是使其穿透到下層, 所以不會再套用到 [_ngcontent-xxx-xxx
ps. 這邊兩段的優先權會採取 selector 較為清楚 第一段
這是 app-component.scss 下的 tile.component.scss
1 | :host { |
compile 後
1 | [_nghost-mhs-c1] h1[_ngcontent-mhs-c1] { |
- 寫在 :host 內也是會被 compile 出封裝後的樣式 - 直接寫在 scss 內, 沒有任何嵌套也是會 compile 出封裝後的樣式 - 如果使用 ::ng-deep 來進行穿透就會突破封裝, 進而引起反寫覆蓋全域的風險
程式碼: 點我
https://indepth.dev/posts/1469/techniques-to-style-component-host-element-in-angular