Angular 从父组件更改子组件样式但不是全局

Angular change child component style from parent component but not globally

我创建了一个基于Mat-table (Angular Material) 的共享组件(<nextgen-table></nextgen-table>)。在项目中使用此组件时,发现我需要更改 table 列的行为(宽度)。

我已经在我的其他组件(比如说 X, Y )中导出了 nextgen-table ,其中 nextgen-table 当然是一个子组件。

要更改 mat-table 特定列的宽度,我必须使用这样的东西:

mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
    flex: 0 0 40%;
    text-align: left;
} 
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
    flex: 0 0 20%;
}

上面的 CSS 代码我在 X.component.css 中实现,我猜是因为封装的原因它没有工作。

经过一些搜索,我发现 只需在 x.component.ts 的组件装饰器中添加 encapsulation: ViewEncapsulation.None 即可正常工作。在这个解决方案之后,我从组件 X 导航到组件 Y,其中我没有实现上面的 CSS 代码。但是组件 Y 有前两列,因为我只想要组件 X,但组件 Y 也有我不想要的组件 Y。

所以我的问题是如何从仅适用于父组件而不适用于其他组件的父组件更新 nextgen-table 的样式。

我也试过用

:host(mat-cell:nth-child(1)){
  flex: 0 0 40%;
  text-align: left;
}

:host(mat-header-cell:nth-child(1)) {
    flex: 0 0 40%;
    text-align: left;
}

但什么都没有 happened/changed。

在此先感谢您的帮助

您可以使用 ::ng-deep pseudo class 来专门针对子元素,而无需更改整个组件的视图封装(这意味着它的所有规则都会泄漏)。

注意::ng-deep自从现在的几个主要版本以来已被标记为已弃用,但在找到解决方法之前他们不会删除支持。

parentX.html

<div class="compContainer">
    <nextgen-table></nextgen-table>
</div>

parentX.scss

::ng-deep .compContainer nextgen-table
{
    mat-cell:nth-child(1),
    mat-header-cell:nth-child(1) {
        flex: 0 0 40%;
        text-align: left;
    } 
    mat-cell:nth-child(2),
    mat-header-cell:nth-child(2) {
        flex: 0 0 20%;
    }
}

您还可以将 css 规则添加到全局 style.scss 文件。

//Rules for parent X
app-parent-componentX .compContainer nextgen-table
{
    mat-cell...
}

//Rules for a parent Y
app-parent-componentY .compContainer nextgen-table
{
    mat-cell...
}

您需要做的就是在 X 或 Y 组件中同时使用 :host::ng-deep 伪 class 选择器。

这是工作 demo

这是快速解释。

<nextgen-table> 编写的样式让 nextgen-table.component.css 被 angular 通过为每个样式添加特定属性来封装。即如果你写了类似

.mat-header-cell{
    background-color: #ff0000;
}

然后就变成了,

.mat-header-cell[_ngcontent-c29]
    background-color: #ff0000;
}

所以我们需要做的就是在我们的组件 X 或组件 Y 中覆盖这个样式。

我们有 ::ng-deep 伪选择器,它将阻止 angular 封装组件的 css。

但是使用 ::ng-deep 也会将我们的 css 泄漏到父组件。所以为了防止我们需要封装出 ::ng-deep 样式。为此,我们可以使用 :host 伪选择器。

所以如果我们在组件 X 中编写以下 css,

:host ::ng-deep .x-table .mat-header-cell{
  background-color: lightblue;
}

然后会变成这样,

[_nghost-c82] .x-table .mat-header-cell {
    background-color: lightblue;
}

现在上面 css 选择的优先级高于写在 table 组件 .mat-header-cell[_ngcontent-c29] 中的样式。

这就是我们如何在任何父组件中覆盖子组件的样式。 我希望这会有所帮助。

更新: 正如您在 Angular's official docs 中看到的那样,::ng-deep 已被弃用。

The shadow-piercing descendant combinator is deprecated and support is being removed from major browsers and tools. As such we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until then ::ng-deep should be preferred for a broader compatibility with the tools.

所以如果你不想依赖 ::ng-deep 而不是,

您可以在您已经尝试过的 <nextgen-table> table 组件中使用 ViewEncapsulation.NoneDemo here

并且为了防止样式渗透到其他组件中,您可以通过在所有样式前面添加选择器来限定 table 样式的范围。

nextgen-table .mat-header-cell{
    background-color: #ff0000;
}

然后对 X 组件执行相同的操作。

  • 使用 ViewEncapsulation.None
  • 禁用视图封装
  • 然后通过编写比 table 的实际样式具有更高特异性的样式来覆盖 table 组件上的样式。

禁用 X 组件中的封装,

@Component({
  selector: "app-x",
  styleUrls: ["x.component.css"],
  templateUrl: "x.component.html",
  encapsulation: ViewEncapsulation.None
})
export class XComponent {

}

然后在x.compoent.css

中覆盖table的组件样式
app-x nextgen-table .mat-header-cell{
  background-color: lightblue;
}

如果你不想禁用视图封装,那么你可以直接将样式写入全局样式表styles.css

请记住,这一切都是关于重写和限定您的样式。