首页 > 解决方案 > Angular:使用 NgIf 进行更改检测问题

问题描述

嘿,我创建了一个演示来大规模演示一个问题。

更新:我已经更新了演示,问题出在 ngIf 上!!

这是演示: https ://stackblitz.com/edit/angular-ivy-bac4pe?file=src%2Fapp%2Flist%2Flist.component.ts

这就是问题所在:我们不仅使用指令作为组件行为的扩展,而且还作为组件的外部 API。我们有一个列表组件,它假设通过分页显示列表的当前项目。我们有一个 paginator 指令,他的工作是为组件公开 Pagination Api。我们有一个分页服务,它根据数据和页面大小执行实际的分页(分页方法)。

我发生了一个非常奇怪的行为:有一个 setInterval 就像轮询一样 - 每 5 秒更新一次数据。当我尝试通过指令->服务-> 更新当前项目列表时,即使我调用了 markforCheck ,项目也没有更新,因为我正在使用推送策略来提高性能。我不想调用detectChanges,因为这是一种不好的做法。

当我尝试使用 markForCheck 通过 Service->Component 更新当前项目列表时,一切正常。

如果有人能详细解释这种现象为什么会发生以及如何解决这个问题,我会很高兴。

标签: angularangular-directiveangular-changedetection

解决方案


发生的事情是一系列原因。

首先,注意它this.cdr.markForCheck()不会运行变更检测,而是将其祖先标记为需要运行变更检测。下次更改检测在任何地方运行时,它也会针对那些被标记的组件运行。

第二个也是更重要的是*ngIf结构。发生更改时*ngIf将首先通知并执行以下操作:( github )

    @Input()
      set ngIf(condition: T) {
        this._context.$implicit = this._context.ngIf = condition;
        this._updateView();
      }
      // ...
      private _updateView() {
        if (this._context.$implicit) {
          if (!this._thenViewRef) {
            this._viewContainer.clear();
            this._elseViewRef = null;
            if (this._thenTemplateRef) {
              this._thenViewRef =
                  this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
            }
          }
        } else {
          if (!this._elseViewRef) {
            this._viewContainer.clear();
            this._thenViewRef = null;
            if (this._elseTemplateRef) {
              this._elseViewRef =
                  this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
            }
          }
        }
      }

如你看到的:

this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);

这意味着它需要dom并重新生成它!在此过程中,您将丢失要渲染的更改数据。

下次父组件数据发生更改并且其更改检测触发时,还会检查子组件中的更改,因为您通过markForCheck()上次调用保留了此检查,再次*ngIf对其容器之前的更改做出反应,因此它获取并用旧数据替换 dom . 就这样继续下去……所以你总是后退一步。


推荐阅读