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

Posted

技术标签:

【中文标题】从父组件角度更改子组件样式但不是全局更改【英文标题】:Angular change child component style from parent component but not globally 【发布时间】:2020-06-28 17:51:47 【问题描述】:

我基于Mat-table(Angular Material)创建了一个共享组件(<nextgen-table></nextgen-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%;

我在 X.component.css 中实现的上述 CSS 代码我猜是因为封装而无法正常工作。

经过一番搜索,我发现solution 只需在x.component.ts 的组件装饰器中添加encapsulation: ViewEncapsulation.None 即可正常工作。在此解决方案之后,我从组件 X 导航到未实现上述 CSS 代码的组件 Y。但是组件 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;

但没有发生/改变。

提前感谢您的帮助

【问题讨论】:

你可以阅读这篇文章也许对你有帮助https://***.com/a/36528769/9148278 【参考方案1】:

您可以使用::ng-deeppseudo 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...

【讨论】:

感谢您的回答,但这并不能解决我的问题:( 你能在 stackblitz 上创建一个例子来显示问题吗?【参考方案2】:

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

这是工作的demo。

这里是快速解释。

&lt;nextgen-table&gt; 编写的样式,比如nextgen-table.component.css,通过为每种样式添加特定属性,由角度封装。也就是说,如果你写过类似的东西,

.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选择比写在表格组件.mat-header-cell[_ngcontent-c29]中的样式优先级更高。

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

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

不推荐使用穿透阴影的后代组合器,支持 被从主要浏览器和工具中删除。因此,我们计划放弃 Angular 中的支持(适用于 /deep/、>>> 和 ::ng-deep 的所有 3 个)。直到 那么 ::ng-deep 应该是首选,以获得更广泛的兼容性 工具。

所以如果你不想依赖::ng-deep,那么

您可以在您已经尝试过的&lt;nextgen-table&gt; 表格组件中使用ViewEncapsulation.None。 Demo here

为了防止样式渗入其他组件,您可以通过在所有样式前面添加选择器来确定表格样式的范围。

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

然后你对你的 X 组件做同样的事情。

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

在你的 X 组件中禁用封装,

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


然后覆盖x.compoent.css中表格的组件样式

app-x nextgen-table .mat-header-cell
  background-color: lightblue;

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

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

【讨论】:

感谢您的出色解决方案,但 ng-deep 将被弃用。有什么替代方案?

以上是关于从父组件角度更改子组件样式但不是全局更改的主要内容,如果未能解决你的问题,请参考以下文章

如何在全局样式表中设置角度4组件选择器的样式?

React js从父组件更改子组件的状态

当组件从父组件动态更改时调用子组件中的方法

Vuex 全局状态更改不会触发 Nuxt 中 v-for 循环中的重新渲染组件

尽管我们在父组件中更改了两次属性的值,但 ngOnChanges 没有被调用两次

vue里面父组件修改子组件样式的方法