如何使用和在哪里使用::ng-deep?

223

如何在Angular 4中使用::ng-deep?它可以用于哪里呢?

实际上,我想从父组件覆盖某些子组件的CSS属性。此外,在IE11上支持吗?


由于/deep/::ng-deep都已被弃用,我建议您查看此答案https://dev59.com/8VYN5IYBdhLWcg3wuKC5#49308475以获取更多详细信息和解决方案的评论。 - Ferie
7个回答

191

通常可以使用/deep/ "穿透" 组合器将样式强制运用到子组件。这个选择器有一个别名 >>> ,现在还有另一个称为::ng-deep的别名。

由于/deep/ 组合器已被弃用,建议使用::ng-deep

例如:

<div class="overview tab-pane" id="overview" role="tabpanel" [innerHTML]="project?.getContent( 'DETAILS')"></div>

css

.overview {
    ::ng-deep {
        p {
            &:last-child {
                margin-bottom: 0;
            }
        }
    }
}

它将应用于子组件


它支持IE11吗? - Jeyabalan Thavamani
2
Angular会进行解析,因此您不必担心兼容性问题。 - Simon_Weaver
1
它只适用于子组件吗?就我所记得的和在另一个评论中看到的,它也适用于组件外的DOM元素。 - yaya
1
我可以确认它也会影响父组件... 我刚遇到这种情况,感到很烦恼。 - Shadoweb

150

我强调限制::ng-deep只应用于组件的子元素,需要在父元素中要求使用封装的CSS类。

为了使其生效,重要的是在父元素之后使用::ng-deep而不是在之前,否则它将在组件加载时立即应用于所有具有相同名称的类。

::ng-deep之前使用:host关键字会自动处理这个问题:

:host ::ng-deep .mat-checkbox-layout

或者,您可以在::ng-deep关键字之前添加组件范围的CSS类来实现相同的行为:

.my-component ::ng-deep .mat-checkbox-layout {
    background-color: aqua;
}

组件模板:

<h1 class="my-component">
    <mat-checkbox ....></mat-checkbox>
</h1>

生成的(Angular 生成的)CSS 将包括唯一生成的名称,并仅应用于其自身的组件实例:

.my-component[_ngcontent-c1] .mat-checkbox-layout {
    background-color: aqua;
}

16
兄弟,你的回答 my-component ::ng-deep... 简直就是救了我的一天。我整天都在尝试使用 ng-deep 给我的组件应用样式,但是我却把所有的组件,甚至整个应用程序的样式都覆盖掉了。 - Cristiano Bombazar
2
值得注意的是: 为了将指定的样式限定在当前组件及其所有后代中[而不是全局范围],请确保在 ::ng-deep 之前包含 :host 选择器。 来源:https://angular.io/guide/component-styles - StvnBrkdll
给出了精确的解释,应该是被接受的答案。 - Timtim

131

使用方法

::ng-deep>>>/deep/可以禁用特定CSS规则的视图封装,换句话说,它可以让你访问不在组件HTML中的DOM元素。例如,如果你正在使用Angular Material(或任何其他类似的第三方库),一些生成的元素位于组件区域之外(比如dialog),你不能直接访问这些元素或使用常规的CSS方法访问它们。如果你想要改变这些元素的样式,你可以使用其中的一个,例如:

::ng-deep .mat-dialog {
  /* styles here */
}

目前Angular团队建议只使用模拟视图封装来进行"deep"操作。

弃用警告

"deep"操作实际上也已经被弃用但是它仍然可以使用,因为Angular提供了预处理支持(不要急于拒绝今天的::ng-deep,先看一下弃用做法)。

无论如何,在使用这种方式之前,我建议您先看一下禁用视图封装方法(这也不是理想的选择,它允许样式泄漏到其他组件中),但在某些情况下,它是更好的方法。 如果您决定禁用视图封装,则强烈建议使用特定的类来避免CSS规则交叉,并最终避免在样式表中造成混乱。 禁用视图封装非常容易,只需在组件的.ts文件中进行即可:

@Component({
  selector: '',
  template: '',
  styles: [''],
  encapsulation: ViewEncapsulation.None  // Use to disable CSS Encapsulation for this component
})

你可以在这篇文章中找到有关视图封装的更多信息。


4
禁用视图封装将使您组件中的所有 CSS 全局应用。 - Vedran
51
不要使用ViewEncapsulation.None!这会使样式可以泄露到其他组件中,从而造成很大的损坏。 - Alex Klaus
补充一点:即使组件被卸载,它也不会卸载CSS,这非常不一致且容易出错。 - Sammaye
“::ng-deep” 功能似乎已经从最新版本的 Chrome 中删除 -> 阅读更多 - Raphaël Balet

39
请确保不要错过 Angular 指南中 ::ng-deep 正上方的 :host-context 的解释: https://angular.io/guide/component-styles。我之前就错过了这个解释,真希望早点看到它。
当你没有编写组件并且无法访问其源代码时,通常需要使用 ::ng-deep,但是当你可以访问组件源代码时,:host-context 可能是一个非常有用的选项。
例如,我在设计的组件中有一个黑色的 <h1> 标题,如果显示在深色主题背景上,我想要将其更改为白色。
如果我无法访问源代码,则可能需要在父级 CSS 中执行此操作:
.theme-dark widget-box ::ng-deep h1 { color: white; }

然而使用:host-context,您可以在组件内部完成此操作。

 h1 
 {
     color: black;       // default color

     :host-context(.theme-dark) &
     {
         color: white;   // color for dark-theme
     }

     // OR set an attribute 'outside' with [attr.theme]="'dark'"

     :host-context([theme='dark']) &
     {
         color: white;   // color for dark-theme
     }
 }
此代码会在组件链的任何位置寻找`.theme-dark`,如果找到,则将CSS应用于h1。这是过度依赖::ng-deep的良好替代方案,而::ng-deep虽然经常必要,但有点反模式。
在本例中,&被h1替换(这就是sass/scss的工作原理),因此您可以将普通和主题/备选CSS定义在相邻的位置,这非常方便。
请注意获取正确的“:”数量。对于::ng-deep,有两个,对于:host-context,只有一个。

如果您不想从任何父组件继承theme-dark,则可以使用:host(.theme-dark)。这完全取决于您的站点CSS设计。此外,属性在CSS中可以非常有用,并且可以以复杂的方式组合,例如:host([theme='dark']:not([dayofweek='tuesday'])) - Simon_Weaver
请注意,这遵循常规的CSS规则,因此如果您有一个如上所述(具有主机上下文CSS)的组件,它位于具有.theme-light类的容器内,而该容器又嵌套在具有.theme-dark的容器内,则仍将选择theme-dark并应用CSS。但是,这是“modernizr”类型类的绝佳解决方案,或者如果您全局设置了主题并且只设置一次。 - Simon_Weaver
我可以使用:host-context代替::ng-deep吗? - eddy
@eddy,我现在太困了,无法完全思考清楚,但是host-context几乎就像ng-deep,但是它是向上遍历DOM树而不是向下。因此,它绝对不等同,但你可以…… - Simon_Weaver

2

更新:

应该使用::ng-deep而不是已经过时的/deep/

根据文档:

穿透影子DOM的后代组合器已被弃用,并且正在从主要浏览器和工具中删除支持。因此,我们计划在Angular中放弃对所有3个选择器(即/deep/,>>>和::ng-deep)的支持。在那之前,应优先考虑使用::ng-deep以获得更广泛的工具兼容性。

您可以在此处找到它。


8
在这段文本中,明确表示::ng-deep也已经被弃用:“我们计划在Angular中取消对/deep/、>>>和::ng-deep的支持”。 - adripanico

0

我查看了所有的答案,发现没有人提到子组件可以从其父组件中传递样式CSS。

在组件的ts文件中,您可以使用以下代码:

   @Input() styles: any = {};  

在组件的 HTML 文件中,您可以使用以下代码:
[ngStyle]="styles"

在父级中,您可以使用以下代码:

<yourComponent [styles]="{backgroundColor: 'blue', 'font-size': '16px'}">

请在此处查看更多详细信息:将样式传递给组件的最佳方法 通过这种方式,我们没有破坏封装性,这是面向对象编程中最重要的原则之一。

-5

谨慎使用 ::ng-deep。我在整个应用程序中使用它来将材料设计工具栏颜色设置为不同的颜色,结果发现当应用程序在测试中时,工具栏颜色会相互干扰。后来发现这是因为这些样式变成了全局的,请参见this article。这里提供一个可行的代码解决方案,不会影响其他组件。

<mat-toolbar #subbar>
...
</mat-toolbar>

export class BypartSubBarComponent implements AfterViewInit {
  @ViewChild('subbar', { static: false }) subbar: MatToolbar;
  constructor(
    private renderer: Renderer2) { }
  ngAfterViewInit() {
    this.renderer.setStyle(
      this.subbar._elementRef.nativeElement, 'backgroundColor', 'red');
  }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接