ChatGPT解决这个技术问题 Extra ChatGPT

如何从父组件的 CSS 文件中设置子组件的样式?

我有一个父组件:

<parent></parent>

我想用子组件填充这个组:

<parent>
  <child></child>
  <child></child>
  <child></child>
</parent>

父模板:

<div class="parent">
  <!-- Children goes here -->
  <ng-content></ng-content>
</div>

子模板:

<div class="child">Test</div>

由于 parentchild 是两个独立的组件,因此它们的样式被锁定在各自的范围内。

在我的父组件中,我尝试做:

.parent .child {
  // Styles for child
}

.child 样式未应用于 child 组件。

我尝试使用 styleUrlsparent 的样式表包含到 child 组件中以解决范围问题:

// child.component.ts
styleUrls: [
  './parent.component.css',
  './child.component.css',
]

但这没有帮助,还尝试了另一种方法,将 child 样式表提取到 parent 中,但这也无济于事。

那么如何设置包含在父组件中的子组件的样式呢?

在我的 answer 中查看完全对范式友好、无需技巧的方式。

T
Top-Master

更新 - 最新方式

不要这样做,如果可以避免的话。正如 Devon Sans 在评论中指出的那样:这个功能很可能会被弃用。

最后更新

Angular 4.3.0 到现在(Angular 12.x),所有穿孔的 css 组合器都被弃用了。 Angular 团队引入了一个新的组合器::ng-deep,如下图所示,

演示:https://plnkr.co/edit/RBJIszu14o4svHLQt563?p=preview

styles: [
    `
     :host { color: red; }
     
     :host ::ng-deep parent {
       color:blue;
     }
     :host ::ng-deep child{
       color:orange;
     }
     :host ::ng-deep child.class1 {
       color:yellow;
     }
     :host ::ng-deep child.class2{
       color:pink;
     }
    `
],



template: `
      Angular2                                //red
      <parent>                                //blue
          <child></child>                     //orange
          <child class="class1"></child>      //yellow
          <child class="class2"></child>      //pink
      </parent>      
    `

老路

您可以使用 encapsulation mode 和/或 piercing CSS combinators >>>, /deep/ and ::shadow

工作示例:http://plnkr.co/edit/1RBDGQ?p=preview

styles: [
    `
     :host { color: red; }
     :host >>> parent {
       color:blue;
     }
     :host >>> child{
       color:orange;
     }
     :host >>> child.class1 {
       color:yellow;
     }
     :host >>> child.class2{
       color:pink;
     }
    `
    ],

template: `
  Angular2                                //red
  <parent>                                //blue
      <child></child>                     //orange
      <child class="class1"></child>      //yellow
      <child class="class2"></child>      //pink
  </parent>      
`

尽管在 Chrome 中不推荐使用穿孔 CSS 组合器
Angular 团队也计划放弃对 ::ng-deep 的支持。从他们的文档中:“不推荐使用穿透阴影的后代组合器,并且正在从主要浏览器和工具中删除支持。因此,我们计划放弃对 Angular 的支持(对于所有 3 个 /deep/、>>> 和: :ng-deep)。在那之前::ng-deep 应该是首选,因为它与工具的兼容性更广泛。 angular.io/guide/component-styles#deprecated-deep--and-ng-deep
只要这是一个公认的答案,人们就会被误导。 ::ng-deep 不应用作上述评论中的@DevonSams 点。
::ng-deep is now deprecated,我不建议在以后的应用程序中使用它
在不提供替代方案的情况下弃用某些东西可能不是最好的解决方案。
C
Carcigenicate

您不应该使用 ::ng-deep,它已被弃用。在 Angular 中,从父组件更改子组件样式的正确方法是使用 encapsulation(阅读下面的警告以了解其含义):

import { ViewEncapsulation } from '@angular/core';

@Component({
    ....
    encapsulation: ViewEncapsulation.None
})

然后,您将能够在不需要 ::ng-deep 的情况下修改组件的 css

.mat-sort-header-container {
  display: flex;
  justify-content: center;
}

警告:这样做将使您为此组件编写的所有 css 规则都是全局的。

为了将您的 css 范围限制在此组件及其子组件中,请将 css 类添加到组件的顶部标签,并将您的 css 放在此标签“内部”:

模板:

<div class='my-component'>
  <child-component class="first">First</child>
</div>,

.scs 文件:

.my-component {
  // All your css goes in there in order not to be global
}

这是 IMO 的最佳答案,因为它实际上是即将弃用的 ::ng-deep 的可行替代方案。通常,组件无论如何都有自己的选择器(<my-component>, <div my-component> 等),因此甚至不需要具有特殊类的包装器元素。
@AlexWalker这可能是适合您的情况的最佳答案,但值得一提的是,它只回答了 OP 问题的一半:此方法允许 CSS 从上到下正常传播,但是,通过丢弃所有封装,不不要将样式限制为特定父母的孩子。如果您以一种方式设置 parent1 的孩子,而另一种设置 parent2 的孩子,那么这些 CSS 规则现在将在这两个地方相互冲突。这可能是令人麻木的痛苦(Angular 增加了封装来避免它)。
@ruffin这正是我在回答中添加警告以了解使用此技术的含义以及如何使用组件上的顶部css标签“手动封装”的原因
@Tonio——是的,同意;是直接回复亚历克斯而不是你。他的评论“所以甚至不需要具有特殊类的包装元素”让我有点害怕。也许对于特定的情况,Angular“浪费”时间支持封装是有原因的。这个答案在特定情况下是一个可行的解决方案,但正如你所说,一般来说是一个潜在的危险。 MatthewB's solution,例如,在保持封装的同时为子组件设置样式(但如果您有不止一代的子组件,它会真的变得混乱)。
我正在尝试实现这一点,但没有奏效-你们中的任何人都可以帮助我吗? - stackoverflow.com/questions/67639611/…
C
Chrillewoodz

更新 3:

::ng-deep 也已被弃用,这意味着您根本不应该这样做。目前尚不清楚这会如何影响您需要从父组件覆盖子组件中的样式。对我来说,如果这被完全删除似乎很奇怪,因为这将如何影响需要在库组件中覆盖样式的库?

如果您对此有任何见解,请发表评论。

更新 2:

由于 /deep/ 和所有其他阴影穿透选择器现已弃用。 Angular 删除了 ::ng-deep,应该使用它来实现更广泛的兼容性。

更新:

如果使用 Angular-CLI,您需要使用 /deep/ 而不是 >>>,否则它将不起作用。

原来的:

在转到 Angular2 的 Github 页面并随机搜索“样式”后,我发现了这个问题:Angular 2 - innerHTML styling

这表示使用在 2.0.0-beta.10>>>::shadow 选择器中添加的东西。

(>>>)(和等效的/deep/)和 ::shadow 是在 2.0.0-beta.10 中添加的。它们类似于 shadow DOM CSS 组合器(已弃用),仅适用于封装:ViewEncapsulation.Emulated,这是 Angular2 中的默认值。它们可能也可以与 ViewEncapsulation.None 一起使用,但由于它们不是必需的,因此只会被忽略。在支持跨组件样式的更高级功能之前,这些组合器只是一个中间解决方案。

所以简单地做:

:host >>> .child {}

parent 的样式表文件中解决了这个问题。请注意,如上面引用中所述,此解决方案只是中间的,直到支持更高级的跨组件样式。


看起来他们将取消对 ::ng-deep angular.io/guide/component-styles#deprecated-deep--and-ng-deep 的支持
至于 2021 年 Angular 团队没有想出任何好的替代品来替代 ng-deep,我认为它不会很快被移除
M
Matthew B.

遗憾的是,/deep/ 选择器似乎已被弃用(至少在 Chrome 中)https://www.chromestatus.com/features/6750456638341120

简而言之,除了以某种方式让您的子组件动态设置样式之外,似乎(目前)没有长期解决方案。

您可以将样式对象传递给您的孩子并通过以下方式应用它:
<div [attr.style]="styleobject">

或者,如果您有特定的样式,您可以使用类似的东西:
{ 2}

与此相关的更多讨论:https://github.com/angular/angular/issues/6511


A
Alexander Abakumov

您不应该为父组件中的子组件元素编写 CSS 规则,因为 Angular 组件是一个自包含的实体,它应该明确声明对外部世界可用的内容。如果将来子布局发生变化,则分散在其他组件的 SCSS 文件中的子组件元素的样式很容易损坏,从而使您的样式非常脆弱。对于 CSS,这就是 ViewEncapsulation 的用途。否则,如果您可以将值分配给面向对象编程中任何其他类的某个类的私有字段,那将是相同的。

因此,您应该做的是定义一组可以应用于子宿主元素的类,并实现子元素如何响应它们。

从技术上讲,可以这样做:

// child.component.html:
<span class="label-1"></span>

// child.component.scss:
:host.child-color-black {
    .label-1 {
        color: black;
    }
}

:host.child-color-blue {
    .label-1 {
        color: blue ;
    }
}

// parent.component.html:
<child class="child-color-black"></child>
<child class="child-color-blue"></child>

换句话说,您使用 Angular 提供的 :host 伪选择器 + 一组 CSS 类来定义子组件本身中可能的子样式。然后,您可以通过将预定义的类应用于 <child> 宿主元素来从外部触发这些样式。


看起来不错的解决方案,是否有 parent.component.scss 文件?如果是,愿意给吗?
@ManoharReddyPoreddy parent.component.scss 中不应有与子组件样式相关的样式。这是这种方法的唯一目的。为什么需要 parent.component.scss
不确定,只是知道一点css。你能在 jsbin 或其他上分享一个完整的解决方案吗?您的解决方案可以成为所有人的未来解决方案。
@ManoharReddyPoreddy 我建议您先在实践中尝试这些代码。然后,如果您遇到任何问题,您将有一个特定的问题,我可以回答或建议您研究特定主题以了解如何解决您的问题。我提到 ViewEncapsulation 只是因为它的默认值是导致 OP 问题的原因。您不必为上述代码分配不同的 ViewEncapsulation
+1 谢谢。将来会回来采用这个解决方案,今天选择 ::ng-deep stackoverflow.com/a/36528769/984471
C
Chrillewoodz

有同样的问题,所以如果你使用 angular2-cli 和 scss/sass 使用 '/deep/' 而不是 '>>>',最后一个选择器还不支持(但适用于 css)。


c
code5

如果您希望比实际的子组件更有针对性,那么您应该执行以下操作。这样,如果其他子组件共享相同的类名,它们就不会受到影响。

Plunker:https://plnkr.co/edit/ooBRp3ROk6fbWPuToytO?p=preview

例如:

import {Component, NgModule } from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>I'm the host parent</h2>
      <child-component class="target1"></child-component><br/>
      <child-component class="target2"></child-component><br/>
      <child-component class="target3"></child-component><br/>
      <child-component class="target4"></child-component><br/>
      <child-component></child-component><br/>
    </div>
  `,
  styles: [`

  /deep/ child-component.target1 .child-box {
      color: red !important; 
      border: 10px solid red !important;
  }  

  /deep/ child-component.target2 .child-box {
      color: purple !important; 
      border: 10px solid purple !important;
  }  

  /deep/ child-component.target3 .child-box {
      color: orange !important; 
      border: 10px solid orange !important;
  }  

  /* this won't work because the target component is spelled incorrectly */
  /deep/ xxxxchild-component.target4 .child-box {
      color: orange !important; 
      border: 10px solid orange !important;
  }  

  /* this will affect any component that has a class name called .child-box */
  /deep/ .child-box {
      color: blue !important; 
      border: 10px solid blue !important;
  }  


  `]
})
export class App {
}

@Component({
  selector: 'child-component',
  template: `
    <div class="child-box">
      Child: This is some text in a box
    </div>
  `,
  styles: [`
    .child-box {
      color: green;    
      border: 1px solid green;
    }
  `]
})
export class ChildComponent {
}


@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, ChildComponent ],
  bootstrap: [ App ]
})
export class AppModule {}

希望这可以帮助!

代码矩阵


i
ilius33

其实还有一种选择。这是相当安全的。您可以使用 ViewEncapsulation.None 但将所有组件样式放入其标签(又名选择器)中。但无论如何总是更喜欢一些全局样式和封装样式。

这是修改后的 Denis Rybalka 示例:

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'parent',
  styles: [`
    parent {
      .first {
        color:blue;
      }
      .second {
        color:red;
      }
    }
 `],
 template: `
    <div>
      <child class="first">First</child>
      <child class="second">Second</child>
    </div>`,
  encapsulation: ViewEncapsulation.None,
})
export class ParentComponent  {
  constructor() { }
}

A
Abdullah Adeeb

由于 /deep/、>>> 和 ::ng-deep 都已弃用。最好的方法是在您的子组件样式中使用以下内容

:host-context(.theme-light) h2 {
  background-color: #eef;
}

这将在您的子组件的任何祖先中查找主题灯。在此处查看文档:https://angular.io/guide/component-styles#host-context


D
Denis Rybalka

在 Angular 中有几个选项可以实现这一点:

1) 您可以使用深度 css 选择器

:host >>> .childrens {
     color: red;
 }

2)您还可以更改视图封装,它默认设置为 Emulated,但可以轻松更改为使用 Shadow DOM 本机浏览器实现的 Native,在您的情况下,您只需禁用它

例如:`

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'parent',
  styles: [`
    .first {
      color:blue;
    }
    .second {
      color:red;
    }
 `],
 template: `
    <div>
      <child class="first">First</child>
      <child class="second">Second</child>
    </div>`,
  encapsulation: ViewEncapsulation.None,
 })
 export class ParentComponent  {
   constructor() {

   }
 }

实际上这意味着样式会影响整个 dom,而不仅仅是子元素。
这种方式完全被 angular & chrome 弃用
R
Rusty Rob

如果您可以访问子组件代码,我发现传递 @INPUT 变量会更简洁:

这个想法是父母告诉孩子它的外观状态应该是什么,孩子决定如何显示状态。这是一个不错的架构

SCSS方式:

.active {
  ::ng-deep md-list-item {
    background-color: #eee;
  }
}

更好的方法: - 使用 selected 变量:

<md-list>
    <a
            *ngFor="let convo of conversations"
            routerLink="/conversations/{{convo.id}}/messages"
            #rla="routerLinkActive"
            routerLinkActive="active">
        <app-conversation
                [selected]="rla.isActive"
                [convo]="convo"></app-conversation>
    </a>
</md-list>

也很难维护,尤其是对于递归组件。
v
vivanov

截至今天(Angular 9),Angular 使用 Shadow DOM 将组件显示为 custom HTML elements。为这些自定义元素设置样式的一种优雅方式可能是使用 custom CSS variables。这是一个通用示例:

类 ChildElement 扩展 HTMLElement { constructor() { super(); var shadow = this.attachShadow({mode: 'open'}); var wrapper = document.createElement('div'); wrapper.setAttribute('class', 'wrapper'); // 创建一些 CSS 应用到 shadow dom var style = document.createElement('style'); style.textContent = ` /* 这里我们定义了变量的默认值 --background-clr */ :host { --background-clr: green; } .wrapper { 宽度:100px;高度:100px;背景颜色:var(--background-clr);边框:1px 纯红色; } `; shadow.appendChild(样式); shadow.appendChild(包装器); } } // 定义新元素 customElements.define('child-element', ChildElement); /* CSS 代码 */ /* 从自定义元素的角度来看,这个元素被称为 :host。注释掉这个 CSS 将导致背景为绿色,正如自定义元素中定义的那样 */ child-element { --background-clr: yellow; }

从上面的代码可以看出,我们创建了一个自定义元素,就像 Angular 对每个组件所做的那样,然后我们从全局范围覆盖自定义元素的阴影根中负责背景颜色的变量.

在 Angular 应用程序中,这可能类似于:

父组件.scss

child-element {
  --background-clr: yellow;
}

子元素.component.scss

:host {
  --background-clr: green;
}

.wrapper {
  width: 100px;
  height: 100px;
  background-color: var(--background-clr);
  border: 1px solid red;
}

m
marc_s

我更喜欢实现以下目标:

使用 @Component 将 css 类添加到宿主元素并将封装设置为无。然后引用在组件中添加到主机的类 style.css.scss 这将允许我们声明只会影响我们自己和我们的类范围内的孩子的样式。铁

@Component({
  selector: 'my-component',
  templateUrl: './my-component.page.html',
  styleUrls: ['./my-component.page.scss'],
  host: {
    class: 'my-component-class'
  },
  encapsulation: ViewEncapsulation.None
})

结合以下 css (my-component.page.scss)

// refer ourselves so we are allowed to overwrite children but not global styles
.my-component-class {
  // will effect direct h1 nodes within template and all h1 elements within child components of the 
  h1 {
    color: red;
  }
}
// without class "scope" will affect all h1 elements globally
h1 {
  color: blue;
}

J
Jed Richards

快速的回答是你根本不应该这样做。它破坏了组件封装并破坏了您从独立组件中获得的好处。考虑将 prop 标志传递给子组件,然后它可以自行决定如何以不同方式呈现或在必要时应用不同的 CSS。

<parent>
  <child [foo]="bar"></child>
</parent>

Angular 正在弃用所有影响父母子女风格的方式。

https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep


好吧,他们在他们的文档中明确表示他们最终会这样做,我猜这意味着他们会这样做。不过我同意,不会很快发生。
所以他们几乎会让他们自己的材料库变得毫无用处。由于每个客户都需要自己的设计,因此我从未能够在任何库中使用默认主题。通常你只想要一个组件的功能。我不能说我理解他们这个决定背后的整体逻辑。
d
d00lar

我也有这个问题,不想使用不推荐使用的解决方案,所以我最终得到:

在父母

 <dynamic-table
  ContainerCustomStyle='width: 400px;'
  >
 </dynamic-Table>

子组件

@Input() ContainerCustomStyle: string;

在 html div 中的子项中

 <div class="container mat-elevation-z8"
 [style]='GetStyle(ContainerCustomStyle)' >

并在代码中

constructor(private sanitizer: DomSanitizer) {  }

  GetStyle(c) {
    if (isNullOrUndefined(c)) { return null; }
    return  this.sanitizer.bypassSecurityTrustStyle(c);
  }

像预期的那样工作,不应该被弃用;)


有趣的!我最终得到了类似的东西(现在)。你从哪里得到 DomSanitizer?编辑:找到它:angular.io/api/platform-browser/DomSanitizer
是的,在 v7 中它是原生的,你只需要在构造函数中请求注入它。 ;) ,在较早的版本中,我不知道它是否存在-我从 v7 开始;)
d
deadflowers

要在子组件中分配元素的类,您只需在子组件中使用 @Input 字符串并将其用作模板内的表达式。下面是我们在共享 Bootstrap 加载按钮组件中更改图标和按钮类型的示例,而不会影响它在整个代码库中的使用方式:

app-loading-button.component.html (子)

<button class="btn {{additionalClasses}}">...</button>

应用程序加载按钮.component.ts

@Input() additionalClasses: string;

父.html

<app-loading-button additionalClasses="fa fa-download btn-secondary">...</app-loading-button>

我认为这样更好:<button class="btn" [ngClass]="additionalClasses">...</button>
L
Luminous

随着互联网的更新,我遇到了一个解决方案。

首先是一些警告。

还是不要这样做。澄清一下,我不会计划允许您为它们设置样式的子组件。 SOC。如果您作为组件设计师想要允许这样做,那么您将拥有更多的权力。如果您的孩子不住在影子 dom 中,那么这对您不起作用。如果您必须支持不能有 shadow dom 的浏览器,那么这对您也不起作用。

首先,将您的子组件的封装标记为阴影,以便它在实际的阴影 dom 中呈现。其次,将 part 属性添加到您希望允许父级设置样式的元素。在父组件样式表中,您可以使用 ::part() 方法来访问


C
Chris Hamilton

这是一个只有 vanilla css 的解决方案,没有什么花哨的,你甚至不需要 !important。我假设你不能修改孩子,否则答案就更简单了,我把答案放在最后以防万一。

在使用库中的预制组件时,有时需要覆盖孩子的 CSS,并且开发人员没有提供任何 class 输入变量。 ::ng-deep 已弃用,encapsulation: ViewEncapsulation.None 将您组件的所有 CSS 设为全局。所以这里有一个简单的解决方案,它不使用这两种方法。

事实上,我们确实需要一个全局样式才能让 CSS 到达子级。因此,我们可以将样式放在 styles.css 中,或者我们可以创建一个新的 CSS 文件并将其添加到 angular.json 中的 styles 数组中。唯一的问题是我们需要一个特定的选择器,以免针对其他元素。这是一个非常简单的解决方案 - 只需在 html 中添加一个唯一的类名,我建议在类名中使用父组件的名称以确保它是唯一的。

父组件

<child class="child-in-parent-component"></child>

假设我们想要更改子元素中所有按钮的背景颜色,我们确实需要实现正确的特异性以确保我们的样式优先。我们可以在所有属性旁边使用 !important 来做到这一点,但更好的方法是重复类名,直到我们的选择器足够具体,可能需要几次尝试。这样,其他人可以在必要时再次覆盖此 css。

全局样式文件

.child-in-parent-component.child-in-parent-component.child-in-parent-component
  button {
  background-color: red;
}

或使用 !important 快速而肮脏(不推荐)

.child-in-parent-component button {
  background-color: red !important;
}

如果子组件可以修改

只需向组件添加一个输入变量并使用 Angular 的 ngStyle 指令。您可以添加多个变量来设置组件的多个区域的样式。

子组件

type klass = { [prop: string]: any } | null;

@Component({...})
export class ChildComponent {
  @Input() containerClass: klass = null;
  @Input() pClass: klass = null;
...
}
<div [ngStyle]="containerClass">
  <p [ngStyle]="pClass">What color will I be?</p>
</div>

父组件

<child
  [containerClass]="{ padding: '20px', 'background-color': 'black' }"
  [pClass]="{ color: 'red' }"
>
</child>

这是创建具有动态样式的组件的预期方式。许多预制组件将具有类似的输入变量。


看看 Vivanov 在这个威胁中提出的解决方案
@Eliseo 如果您使用库中的组件,则不适用。如果您有权访问源代码,则可以使用与 ngStyle 指令结合的输入变量。 angular.io/api/common/NgStyle
@Eliseo 我编辑了我的答案以向您展示我的意思。
A
AndreaM16

我提出了一个更清楚的例子,因为 angular.io/guide/component-styles 指出:

不推荐使用穿透阴影的后代组合器,并且正在从主要浏览器和工具中删除支持。因此,我们计划放弃对 Angular 的支持(对 /deep/、>>> 和 ::ng-deep 的所有 3 个)。在此之前 ::ng-deep 应该是首选,以便与工具更广泛地兼容。

如果需要,在 app.component.scss 上导入您的 *.scss_colors.scss 有一些常见的颜色值:

$button_ripple_red: #A41E34;
$button_ripple_white_text: #FFF;

将规则应用于所有组件

所有具有 btn-red 类的按钮都将被设置样式。

@import `./theme/sass/_colors`;

// red background and white text
:host /deep/ button.red-btn {
    color: $button_ripple_white_text;
    background: $button_ripple_red;
}

将规则应用于单个组件

app-login 组件上具有 btn-red 类的所有按钮都将被设置样式。

@import `./theme/sass/_colors`;

/deep/ app-login button.red-btn {
    color: $button_ripple_white_text;
    background: $button_ripple_red;
}

E
E_net4 - Mr Downvoter

我已经在 Angular 之外解决了它。我已经定义了一个共享的 scss,我要导入我的孩子。

共享.scss

%cell {
  color: #333333;
  background: #eee;
  font-size: 13px;
  font-weight: 600;
}

孩子.scss

@import 'styles.scss';
.cell {
  @extend %cell;
}

我提出的方法是一种如何解决 OP 提出的问题的方法。正如在多个场合提到的,::ng-deep、:ng-host 将被贬低,在我看来,禁用封装只是太多的代码泄漏。


m
muhammed suhair

让 'parent' 是 parent 的类名, 'child' 是 child 的类名

.parent .child{
//css definition for child inside parent components
} 

您可以使用此格式将 CSS 格式定义为“父”内的“子”组件