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

时间:2016-04-10 08:35:29

标签: css angular

我有一个父组件:

<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来尝试另一种方式,但这也没有帮助。

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

17 个答案:

答案 0 :(得分:179)

更新 - 最新方式

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

更新 - 更新方式

Angular 4.3.0 ,不推荐使用所有穿孔css组合器。 Angular团队推出了一个新的组合器 ::ng-deep(仍处于实验级别,而不是完整和最终的方式),如下所示,

DEMO: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>      
`

答案 1 :(得分:15)

有同样的问题,所以如果你使用angular2-cli和scss / sass一起使用&#39; / deep /&#39;而不是&#39;&gt;&gt;&gt;&#39;,最后的选择器还没有得到支持(但是对css很有用)。

答案 2 :(得分:14)

可悲的是,似乎已弃用/ deep / selector(至少在Chrome中) https://www.chromestatus.com/features/6750456638341120

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

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

或者,如果您有特定的风格,您可以使用以下内容: <div [style.background-color]="colorvar">

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

答案 3 :(得分:10)

如果您希望更多地定位到实际的子组件,则应该遵循以下步骤。这样,如果其他子组件共享相同的类名,则不会受到影响。

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

希望这有帮助!

codematrix

答案 4 :(得分:9)

如果你不想使用:: ng-deep,你可以这样做,这似乎是正确的方法:

  Project.json:
{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.WindowsAzure.Storage": "9.0.0"
      }
    }
   }
}

然后,您将能够在不需要:: ng-deep

的情况下修改组件的css
import { ViewEncapsulation } from '@angular/core';

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

警告:请注意,如果您的组件有很多孩子,您为此组件编写的CSS可能会影响所有孩子!

答案 5 :(得分:5)

从今天(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;
}

答案 6 :(得分:4)

您不应该为父组件中的子组件元素编写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 + CSS类集提供的:host伪选择器在子组件本身中定义可能的子样式。然后,您可以通过将预定义的类应用于<child>宿主元素来从外部触发这些样式。

答案 7 :(得分:4)

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

答案 8 :(得分:1)

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

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

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

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

答案 9 :(得分:1)

实际上还有另一种选择。这比较安全。您可以使用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() { }
}

答案 10 :(得分:1)

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

尽可能

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

工作正常,不应弃用;)

答案 11 :(得分:1)

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

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

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

app-loading-button.component.ts

@Input() additionalClasses: string;

parent.html

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

答案 12 :(得分:0)

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

首先需要注意的一点。

  1. 还是不要这样做。为了澄清,我不会计划允许您设置子组件样式的子组件。 SOC。如果您作为组件设计者希望允许这样做,那么您将获得更多的权力。
  2. 如果您的孩子不住在暗影之屋,那么这对您不起作用。
  3. 如果您必须支持没有影子dom的浏览器,那么这对您也不起作用。

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

答案 13 :(得分:0)

我提出了一个示例,以便更清楚,因为angular.io/guide/component-styles声明:

  

不推荐使用阴影穿透后代组合器,并且正在从主要浏览器和工具中删除支持。因此,我们计划放弃Angular中的支持(对于/ deep /,&gt;&gt;&gt;和:: 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;
}

将规则应用于单个组件

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

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

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

答案 14 :(得分:0)

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

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

这将在您的子组件的任何祖先中寻找主题灯。 在此处查看文档:{​​{3}}

答案 15 :(得分:-1)

我已经在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将被弃用,并且禁用封装在我看来是太多的代码泄漏。

答案 16 :(得分:-1)

让'parent'为父级的类名,'child'为孩子的类名

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

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