问题的产生与描述

在使用NG-ZORRO组件的过程中,需要根据产品的需要,在某些页面修改某些NZ组件的样式。但是由于angular默认采用了Emulated的视图封装模式,导致即便我们想在父组件中直接去重写子组件的样式,也无法办到。

例如AppComponent是一个"父组件",其内包含Comp1Component与Comp2Component两个"子组件"。即使我在AppComponent中添加样式如下样式,然而组件1的字体颜色依然没有变成红色。设置在控制台选中组件1,styles中根本没有对应的样式。

.f-red{
    color: red;
}

/* 企图修改组件1的字体颜色 */
#comp1{
    color: red;
}

问题分析

angular组件的CSS样式被封装进了自己的视图中,而不会影响到应用程序的其它组件,这个效果在大部分时候非常的有用,我们不用担心某个组件的代码会污染其子组件,但当我们想改变第三方库的样式的时候也非常的麻烦。angular的视图封装模式默认是Emulated 模式,以下是官方对其功能的描述。

通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,以达到把 CSS 样式局限在组件视图中的目的。

其实如果在html中搜索#comp1,会得到如下的结果。angular默认的为每个组件添加了一个属性,同样也为css限定了对应的属性。这也是为什么在"父组件"修改"子组件"不生效,因为根本找不到#comp1[_ngcontent-haw-c0]选择器对应的元素。

解决办法

可以使用 /deep/>>>::ng-deep选择器来强制一个样式对各级子组件的视图也生效,它不但会作用于组件的子视图,也会作用于投影进来的内容(ng-content)。

.f-red{
    color: red;
}

/* 企图修改组件1的字体颜色 */
/* :host ::ng-deep #comp1{
    color: red;
} */
:host /deep/ #comp1{
    color: red;
}

需要注意的是CSS标准中用于"刺穿 Shadow DOM"的组合器已经被废弃,并将这个特性从主流浏览器和工具中移除。angular官方建议统一使用 ::ng-deep,以便兼容将来的工具。