【问题标题】:How to style child components from parent component's CSS file?如何从父组件的 CSS 文件中设置子组件的样式?
【发布时间】:2016-07-31 08:53:13
【问题描述】:

我有一个父组件:

<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 中,但这也没有帮助。

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

【问题讨论】:

标签: css angular angular-components


【解决方案1】:

更新 3:

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

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

更新 2:

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

更新:

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

原文:

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

说要使用在2.0.0-beta.10&gt;&gt;&gt;::shadow 选择器中添加的东西。

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

所以简单地做:

:host >>> .child {}

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

【讨论】:

【解决方案2】:

更新 - 最新方式

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

上次更新

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 &gt;&gt;&gt;, /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>      
`

【讨论】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

    或者,如果您有特定的风格,您可以使用类似:
    &lt;div [style.background-color]="colorvar"&gt;

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

    【讨论】:

      【解决方案5】:

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

      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 {}
      

      希望这会有所帮助!

      代码矩阵

      【讨论】:

        【解决方案6】:

        在 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 完全弃用
        【解决方案7】:

        我提出了一个更清楚的例子,因为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;
        }
        

        【讨论】:

          【解决方案8】:

          如果您可以访问子组件代码,我发现传递@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>
          

          【讨论】:

          • 也很难维护,尤其是对于递归组件。
          【解决方案9】:

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

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

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

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

          【讨论】:

          • 嗯,他们已经在他们的文档中明确表示他们最终会这样做,我想这意味着他们会这样做。不过我同意,不会很快发生。
          • 所以他们几乎会让他们自己的材料库变得无用。我从来没有在任何库中使用默认主题,因为每个客户都需要自己的设计。通常你只想要一个组件的功能。我不能说我理解他们做出这个决定的整体逻辑。
          【解决方案10】:

          您不应该使用::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>,
          

          Scss 文件:

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

          【讨论】:

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

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

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

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

          // 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 类来定义子组件本身中可能的子样式。然后,您可以通过将预定义的类应用于 &lt;child&gt; 宿主元素来从外部触发这些样式。

          【讨论】:

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

          其实还有一种选择。这是相当安全的。您可以使用 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() { }
          }
          

          【讨论】:

            【解决方案13】:

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

            在父母中

             <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 开始 ;)
            【解决方案14】:

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

            shared.scss

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

            child.scss

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

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

            【讨论】:

              【解决方案15】:

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

              首先有一些注意事项。

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

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

              【讨论】:

                【解决方案16】:

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

                class ChildElement extends HTMLElement {
                  constructor() {
                    super();
                    
                    var shadow = this.attachShadow({mode: 'open'});
                    var wrapper = document.createElement('div');
                    wrapper.setAttribute('class', 'wrapper');
                    
                    // Create some CSS to apply to the shadow dom
                    var style = document.createElement('style');
                    
                    style.textContent = `
                    
                      /* Here we define the default value for the variable --background-clr */
                      :host {
                        --background-clr: green;
                      }
                      
                      .wrapper {
                        width: 100px;
                        height: 100px;
                        background-color: var(--background-clr);
                        border: 1px solid red;
                      }
                    `;
                    
                    shadow.appendChild(style);
                    shadow.appendChild(wrapper);
                  }
                }
                
                // Define the new element
                customElements.define('child-element', ChildElement);
                /* CSS CODE */
                
                /* This element is referred as :host from the point of view of the custom element. Commenting out this CSS will result in the background to be green, as defined in the custom element */
                
                child-element {
                  --background-clr: yellow; 
                }
                <div>
                  <child-element></child-element>
                </div>

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

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

                parent.component.scss

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

                child-element.component.scss

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

                【讨论】:

                  【解决方案17】:

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

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

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

                  app-loading-button.component.ts

                  @Input() additionalClasses: string;
                  

                  父.html

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

                  【讨论】:

                  • 我觉得这样更好:&lt;button class="btn" [ngClass]="additionalClasses"&gt;...&lt;/button&gt;
                  【解决方案18】:

                  设'parent'为父类名,'child'为子类名

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

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

                  【讨论】:

                    【解决方案19】:

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

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

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

                    【讨论】:

                      【解决方案20】:

                      我更喜欢实现以下目标:

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

                      @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;
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 2021-09-27
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-09-19
                        • 2021-12-27
                        • 2018-09-06
                        • 2020-11-17
                        • 2020-01-14
                        相关资源
                        最近更新 更多