我有一个父组件:
<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>
由于 parent
和 child
是两个独立的组件,因此它们的样式被锁定在各自的范围内。
在我的父组件中,我尝试做:
.parent .child {
// Styles for child
}
但 .child
样式未应用于 child
组件。
我尝试使用 styleUrls
将 parent
的样式表包含到 child
组件中以解决范围问题:
// child.component.ts
styleUrls: [
'./parent.component.css',
'./child.component.css',
]
但这没有帮助,还尝试了另一种方法,将 child
样式表提取到 parent
中,但这也无济于事。
那么如何设置包含在父组件中的子组件的样式呢?
更新 - 最新方式
不要这样做,如果可以避免的话。正如 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>
`
您不应该使用 ::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
}
::ng-deep
的可行替代方案。通常,组件无论如何都有自己的选择器(<my-component>, <div my-component>
等),因此甚至不需要具有特殊类的包装器元素。
更新 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
的样式表文件中解决了这个问题。请注意,如上面引用中所述,此解决方案只是中间的,直到支持更高级的跨组件样式。
遗憾的是,/deep/ 选择器似乎已被弃用(至少在 Chrome 中)https://www.chromestatus.com/features/6750456638341120
简而言之,除了以某种方式让您的子组件动态设置样式之外,似乎(目前)没有长期解决方案。
您可以将样式对象传递给您的孩子并通过以下方式应用它:
<div [attr.style]="styleobject">
或者,如果您有特定的样式,您可以使用类似的东西:
{ 2}
与此相关的更多讨论:https://github.com/angular/angular/issues/6511
您不应该为父组件中的子组件元素编写 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
中不应有与子组件样式相关的样式。这是这种方法的唯一目的。为什么需要 parent.component.scss
?
ViewEncapsulation
只是因为它的默认值是导致 OP 问题的原因。您不必为上述代码分配不同的 ViewEncapsulation
。
有同样的问题,所以如果你使用 angular2-cli 和 scss/sass 使用 '/deep/' 而不是 '>>>',最后一个选择器还不支持(但适用于 css)。
如果您希望比实际的子组件更有针对性,那么您应该执行以下操作。这样,如果其他子组件共享相同的类名,它们就不会受到影响。
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 {}
希望这可以帮助!
代码矩阵
其实还有一种选择。这是相当安全的。您可以使用 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() { }
}
由于 /deep/、>>> 和 ::ng-deep 都已弃用。最好的方法是在您的子组件样式中使用以下内容
:host-context(.theme-light) h2 {
background-color: #eef;
}
这将在您的子组件的任何祖先中查找主题灯。在此处查看文档:https://angular.io/guide/component-styles#host-context
在 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() {
}
}
如果您可以访问子组件代码,我发现传递 @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>
截至今天(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;
}
我更喜欢实现以下目标:
使用 @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;
}
快速的回答是你根本不应该这样做。它破坏了组件封装并破坏了您从独立组件中获得的好处。考虑将 prop 标志传递给子组件,然后它可以自行决定如何以不同方式呈现或在必要时应用不同的 CSS。
<parent>
<child [foo]="bar"></child>
</parent>
Angular 正在弃用所有影响父母子女风格的方式。
https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep
我也有这个问题,不想使用不推荐使用的解决方案,所以我最终得到:
在父母
<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);
}
像预期的那样工作,不应该被弃用;)
要在子组件中分配元素的类,您只需在子组件中使用 @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>
随着互联网的更新,我遇到了一个解决方案。
首先是一些警告。
还是不要这样做。澄清一下,我不会计划允许您为它们设置样式的子组件。 SOC。如果您作为组件设计师想要允许这样做,那么您将拥有更多的权力。如果您的孩子不住在影子 dom 中,那么这对您不起作用。如果您必须支持不能有 shadow dom 的浏览器,那么这对您也不起作用。
首先,将您的子组件的封装标记为阴影,以便它在实际的阴影 dom 中呈现。其次,将 part 属性添加到您希望允许父级设置样式的元素。在父组件样式表中,您可以使用 ::part() 方法来访问
这是一个只有 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>
这是创建具有动态样式的组件的预期方式。许多预制组件将具有类似的输入变量。
ngStyle
指令结合的输入变量。 angular.io/api/common/NgStyle
我提出了一个更清楚的例子,因为 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;
}
我已经在 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 将被贬低,在我看来,禁用封装只是太多的代码泄漏。
让 'parent' 是 parent 的类名, 'child' 是 child 的类名
.parent .child{
//css definition for child inside parent components
}
您可以使用此格式将 CSS 格式定义为“父”内的“子”组件
::ng-deep
is now deprecated,我不建议在以后的应用程序中使用它