MatExpansionPanel expression has changed after it was checked error 'mat-expanded: true'

喜你入骨 提交于 2020-03-22 03:38:13

问题


I have an Angular Material Expansion Panel Query list:

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

and a simple array:

questions: [];

The Expansion Panels are generated with an *ngFor: simplified for example:

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

When I extend the questions array, I want to open up the last expansion panel.

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

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

This works just fine - I insert an item to the array,
the ExpansionPanels get's re-rendered, a new Panel gets created and it actually opens up - my problem is that it generates the following error in the console:

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

Is there any way to avoid this?
I have tried using changeDetectorRef and markForCheck() in the subscription, but the error message did not go away (and honestly, I am 100% sure what's the exact problem here).

Update: Stackblitz example of the issue (click the "+" button)


回答1:


From angularindepth:

This is a cautionary mechanism put in place to prevent inconsistencies between model data and UI so that erroneous or old data are not shown to a user on the page.

A running Angular application is a tree of components. During change detection Angular performs checks for each component which consists of the following operations performed in the specified order:

  • update bound properties for all child components/directives
  • call ngOnInit, OnChanges and ngDoCheck lifecycle hooks on all child components/directives
  • update DOM for the current component
  • run change detection for a child component
  • call ngAfterViewInit lifecycle hook for all child components/directives

After each operation Angular remembers what values it used to perform an operation. They are stored in the oldValues property of the component view.

You might want to trigger the lifecycle hook of your component before the DOM update operation..

That should work for you:

You can add a constructor for changedetection (cd) and call it after 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');
    }
}



回答2:


I have found the solution to the problem.

From the link that @iLuvLogix mentioned, forcing a change detection solved the issue:

    constructor(private cd: ChangeDetectorRef) {}

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



回答3:


You can use javascript to achieve the solution
Stackblitz Demo with javascript only

After updating the questions array, you can use javascript,
and i am using setTimeout(()=>{}, 0) to put this task at the end of the event-loop
so its executed after angular is done performing the DOM manipulation.

  this.questions.push('some-new-item');
        setTimeout(
          ()=>{
           const lastExpansionPanel:HTMLElement =   
           document.getElementsByTagName('mat-expansion-panel')
           [document.getElementsByTagName('mat-expansion-panel').length-1]
           .querySelector('mat-expansion-panel-header');

          lastExpansionPanel.click();
          }, 0
        );


来源:https://stackoverflow.com/questions/54823152/matexpansionpanel-expression-has-changed-after-it-was-checked-error-mat-expande

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!