2
votes

Objective: If user clicks any top level mat-expansion-panel-header element then collapse any open nested level mat-expansion-panel-header elements, eg:

  1. Click Main heading 1 to expand it
  2. Click Subheading 1 to expand it
  3. Click Main heading 1 to collapse it. This should fire an event that collapses Subheading 1.

This is essentially a 'housekeeping' task to clean up if user leaves panels expanded.

Issue: If you double click one of the top level mat-expansion-panel-header elements in quick succession (eg: whilst the expand/collapse animations are still running), the following error is thrown in the console:

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

If you continue to fire these errors, eventually it keeps firing indefinitely and the browser completely locks up.

See Stackblitz project here: https://stackblitz.com/edit/expression-changed-error

I was considering doing something like disabling clicks until the open/close animation has finished, but I'm sure there must be a reason that this is happening and a proper fix for it.

3
your stackblitz demo is running fine there is no errorjitender
error occurs only on double click.Narayanan Ramanathan
Yes, only occurs if you double click quickly (whilst expand/collapse animations are still running)sad muso

3 Answers

4
votes

Please remove the following code from html to resolve the error.

(afterExpand)="collapseSub(panel)"

From collapseSub function I see that you are trying to close the panels and open only the selected one. But this is already handled by default. You are trying to change the expanded state of panel when it is already handled by default;

1
votes

if you dont want to remove (afterExpand)="collapseSub(panel)", then you can try this solution.

constructor(private fb: FormBuilder, private cdRef: ChangeDetectorRef) {
  }

collapseSub = (panel: MatExpansionPanel) => {
    const items = this.matExpansionPanelQueryList.filter(item => item.expanded === true);
    if (items) {
      items.forEach(item => {
        item.close();
      });
    }
    panel.open();
    this.cdRef.detectChanges();
  }

this.cdRef.detectChanges(); adding this line will do the trick for you.

1
votes

The simple fix is to schedule your changes after the end of the current event cycle using setTimeout:

collapseSub = (panel: MatExpansionPanel) => {
  setTimeout(() => {
    const items = this.matExpansionPanelQueryList.filter(item => item.expanded === true);
    if (items) {
      items.forEach(item => {
        item.close();
      });
    }
    panel.open();
  });
}