【问题标题】:MatExpansionPanel expression has changed after it was checked error 'mat-expanded: true'MatExpansionPanel 表达式在检查错误 'mat-expanded: true' 后已更改
【发布时间】:2023-03-25 23:00:01
【问题描述】:

我有一个 Angular 材质扩展面板查询列表:

  @ViewChildren(MatExpansionPanel)
  matExpansionPanelQueryList: QueryList<MatExpansionPanel>;

还有一个简单的数组:

questions: [];

扩展面板使用*ngFor 生成:简化例如:

   <ng-container *ngFor="let question of questions>
            <mat-expansion-panel
               ...

当我扩展问题数组时,我想打开最后一个扩展面板。

     extendQuestion() {
        this.matExpansionPanelQueryList.changes.subscribe(
          change => {
            change.last.open();
          }
        );

        this.questions.push('some-new-item');
      }

这很好用 - 我在数组中插入一个项目,
ExpansionPanels 得到重新渲染,一个新的面板被创建并且它实际上打开了 - 我的问题是它在控制台中生成以下错误:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has 
changed after it was checked. Previous value: 'mat-expanded: false'. Current 
value: 'mat-expanded: true'.

有什么办法可以避免这种情况吗?
我曾尝试在订阅中使用 changeDetectorRefmarkForCheck(),但错误消息并没有消失(老实说,我 100% 确定这里的确切问题是什么)。

更新Stackblitz example of the issue (click the "+" button)

【问题讨论】:

  • 请创建一个 stackblitz 演示
  • 这可能有助于解决您的问题并让您深入了解change detection operations
  • @AbhishekKumar 添加了一个示例。

标签: angular angular-material


【解决方案1】:

来自angularindepth

这是一种警示机制,用于防止模型数据和 UI 之间出现不一致,从而不会在页面上向用户显示错误或旧数据。

一个正在运行的 Angular 应用程序是一个组件树。在变更检测期间,Angular 会对每个组件进行检查,这些组件包括按指定顺序执行的以下操作:

  • 更新所有子组件/指令的绑定属性
  • 在所有子组件/指令上调用 ngOnInit、OnChanges 和 ngDoCheck 生命周期挂钩
  • 更新当前组件的 DOM
  • 为子组件运行更改检测
  • 为所有子组件/指令调用 ngAfterViewInit 生命周期钩子

在每次操作之后,Angular 都会记住它用来执行操作的值。它们存储在组件视图的 oldValues 属性中。

您可能希望在 DOM 更新操作之前触发组件的生命周期挂钩..

这应该适合你:

您可以添加constructor 用于更改检测(cd)并在change.last.open(); 之后调用它

import {ChangeDetectorRef, Component, OnInit, QueryList, ViewChildren} from '@angular/core';

import {MatDialog, MatExpansionPanel} from '@angular/material';

@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})

export class AppComponent {
questions = ['first', 'second'];

constructor(private cd: ChangeDetectorRef) {
    }

@ViewChildren(MatExpansionPanel) matExpansionPanelQueryList: QueryList<MatExpansionPanel>;


extendQuestion() {
        this.matExpansionPanelQueryList.changes.subscribe(
        change => {
            change.last.open();
            this.cd.detectChanges();
        }
        );
        this.questions.push('some-new-item');
    }
}

【讨论】:

  • 感谢您提供的信息,但是,您能否详细说明如何在确切的问题中将其付诸实践?如何触发“生命周期钩子”(哪个?),因为我想在新元素被渲染后执行操作(因为我订阅了查询列表更改)?
  • 等一下,我会尽力为您提供示例的实际代码
  • 您发布的解决方案与我在您编辑前 15 分钟写的完全相同?
  • @ForestG 正忙于尝试 stackblitz 并没有看到您已在下面发布答案,因为我的编辑答案仍处于打开状态;)
  • @ForestG 随时为您服务 ;)
【解决方案2】:

我已经找到了问题的解决方案。

从@iLuvLogix 提到的链接中,强制更改检测解决了这个问题:

    constructor(private cd: ChangeDetectorRef) {}

    this.matExpansionPanelQueryList.changes.subscribe(
      change => {
        change.last.open();
        this.cd.detectChanges();
      }
    );

【讨论】:

  • 如果有人遇到同样的问题,您可能想接受我精心制作的答案以供将来参考 - köszönöm ;)
猜你喜欢
  • 2019-05-14
  • 2018-05-10
  • 2018-05-11
  • 2017-06-23
  • 2020-06-29
  • 1970-01-01
  • 2019-01-13
  • 2017-01-24
  • 1970-01-01
相关资源
最近更新 更多