【BUG】解决::ng-deep的定位失败及样式污染问题

:host ::ng-deep .ant-modal-body定位失败问题

问题描述

这个问题是在修改nzModal的内置样式过程中遇到的,欲修改内容为 nzModal组件中,content的pading、margin以及nzModal中子组件的样式等

这些修改并不能直接通过官方提供的api进行修改,但也不是通过标题中的:host ::ng-deep .ant-modal-body进行修改的。虽然:host ::ng-deep .ant-modal-body无法生效,但需要先理解这组选择器的含义。

选择器:host ::ng-deep .ant-modal-body由三部分构成::host的官方表述(Angular - 组件样式)是:"每个组件都会关联一个与其组件选择器相匹配的元素。这个元素称为宿主元素,模板会渲染到其中。:host 伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的那些元素。"我个人理解是,:host就是本组件的根选择器。一般来说,:host后面还会加上本组件中的其他DOM元素,表示样式限定在本组件样式中。

::ng-deep也是伪选择器,不指代实际的DOM元素,表示选择器的深入关系。在应用中,一般是A ::ng-deep B的形式,表示从父级选择器A开始,逐层递进,样式的最终目标作用点是选择器B。A和B之间可能间隔多层DOM元素。

.ant-modal-body是nzModal中的内部封装的一个class。利用浏览器的开发者工具能观察到,如下所示的红色部分即为.ant-modal-body类。这个类跟普通的DOM元素一样,只要能在css文件中正确选择出来,就能非常方便的修改这个页面。其他封装好的zorro组件也能通过这个方式查看类名。

因此,:host ::ng-deep .ant-modal-body选择器就表示在当前组件中,选择类名为.ant-modal-body的DOM元素。

看起来确实能够解决问题,但实际上并未生效。这说明:host中并没有.ant-modal-body类

第二次尝试 ::ng-deep .ant-modal-body

既然:host中并没有.ant-modal-body,那么去掉:host,直接使用::ng-deep .ant-modal-body. 结果表明已经生效.问题看似已经解决,但是实际上,其他组件中的modal也被渲染了,即污染了别的组件.这是由于::ng-deep左边未赋选择器,则表示在解析后的整个html文件中,寻找类名为.ant-modal-body的目标DOM,但是其他组件中也包含了这个nzModal,因此也有这个类.ant-modal-body . 这显然不符合需求.

因此,需要进一步细化.ant-modal-body的匹配程度.如果能够找到这个组件中.ant-modal-body的父级DOM,并为其特殊命名,最终形成这样的格式"::ng-deep 父级选择器 .ant-modal-body ",应该能解决问题.

第三次尝试 ::ng-deep 父级选择器  .ant-modal-body

这次将整个nz-Modal标签设置类名:class="myClass",设置样式如下:::ng-deep .myClass .ant-modal-body.最后发现,仍然不生效.思路正确,但结果不对,应该是class设置错误.

第四次尝试 ::ng-deep 父级选择器  .ant-modal-body

千辛万苦查阅资料后,大佬(https://github.com/NG-ZORRO/ng-zorro-antd/issues/5955)给出了一个解决方案:设置class方式为:nzClassName="myClass",而不是class="myClass".最后,使用::ng-deep .myClass .ant-modal-body即可成功.

原因分析:为什么host不包含ant-modal-body?
失败原因
:host ::ng-deep .ant-modal-bodyhost不包含ant-modal-body
::ng-deep .ant-modal-body污染其他组件
::ng-deep .myClass .ant-modal-body(其中myClass为普通class)普通class不包含ant-modal-body
::ng-deep .myClass .ant-modal-body(其中myClass为ngClass)成功

上表总结了四次尝试,有几个关键问题值得探究:

为什么host不包含ant-modal-body?

其实host的作用范围是<app-root>(参看css - Modal Dialog of different widths are not applied with :host and :ng-deep - Stack Overflow),这一点可以在浏览器中查看渲染后的html页面,modal与app-root实际上是并列关系,而不是包含关系(下图展示了app-root和modal的位置关系),这与代码中,modal的位置无关.第三次尝试的失败也是这一原因,因为普通class仍属于app-root,那也就不包含ant-modal-body了.

本次BUG使用的示例代码

为演示""ng-deep的渗透作用,定义了两个component:APPComponent和AnotherComponent.如下:

app.component.ts

export class AppComponent {
  isVisible = false;

  constructor(private modalService: NzModalService) { }

  showModal(): void {
    this.isVisible = true;
  }

  handleOk(): void {
    this.isVisible = false;
  }

  handleCancel(): void {
    this.isVisible = false;
  }

  showConfirm(): void {
    this.modalService.confirm({
      nzTitle: 'Confirm',
      nzContent: 'Bla bla ...',
      nzOkText: 'OK',
      nzCancelText: 'Cancel'
    });
  }
}

app.component.html

<div class="div-button">
  <button nz-button nzType="primary" (click)="showModal()">AnotherModal</button>
  <nz-modal [(nzVisible)]="isVisible" nzTitle="Modal" nzOkText="Ok" nzCancelText="Cancel" (nzOnOk)="handleOk()"
    (nzOnCancel)="handleCancel()">
    <ng-container *nzModalContent>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
    </ng-container>
  </nz-modal>
</div>
<br />

app.component.less

::ng-deep .myClass .ant-modal-body {
    padding: 0;
    height: 500px;
    width: 100%;
    background-color: red;
}

:host {
    font-size: 28px;
}

another.component.ts

export class AnotherComponent {

  isVisible = false;

  constructor(private modalService: NzModalService) { }

  showModal(): void {
    this.isVisible = true;
  }

  handleOk(): void {
    this.isVisible = false;
  }

  handleCancel(): void {
    this.isVisible = false;
  }

  showConfirm(): void {
    this.modalService.confirm({
      nzTitle: 'Confirm',
      nzContent: 'Bla bla ...',
      nzOkText: 'OK',
      nzCancelText: 'Cancel'
    });
  }

}

another.component.html

<div class="div-button">
  <button nz-button nzType="primary" (click)="showModal()">AnotherModal</button>
  <nz-modal [(nzVisible)]="isVisible" nzTitle="Modal" nzOkText="Ok" nzCancelText="Cancel" (nzOnOk)="handleOk()"
    (nzOnCancel)="handleCancel()">
    <ng-container *nzModalContent>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
      <p>Bla bla ...</p>
    </ng-container>
  </nz-modal>
</div>
<br />

another.component.less为空

ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'false'. Current value: 'true'. Expression location: UserManageComponent component.

翻译过来,就表示angular在检查了相关属性后,又变更了这个属性值或变量值.

这个报错对功能暂时没有影响,报错来源:同一个页面先后两次调用同一个modal,分别用来新增和编辑.当先点击编辑,再点击新增时,就会报错.这主要是由于,编辑时,传递了当前表单初始值,angular完成校验工作后,又打开新增页面,这时就会报错.

网上有一些解决办法:

angular - ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined' - Stack Overflowicon-default.png?t=N7T8https://stackoverflow.com/questions/45467881/expressionchangedafterithasbeencheckederror-expression-has-changed-after-it-was我的做法是,在编辑界面的取消函数中,重置表单信息.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值