angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.4k stars 6.76k forks source link

bug(mat-checkbox): `detectChanges` does not work in `detached` mode #25991

Open scharf opened 2 years ago

scharf commented 2 years ago

The previous version in which this bug was not present was

No response

Description

Running detectChanges() for components with changeDetection: ChangeDetectionStrategy.OnPush does not work as expected

Reproduction

Here is a simple stackblitz to test the Problem (https://stackblitz.com/edit/angular-4csuvx-hrjdpp?file=src/app/checkbox-reactive-forms-example.ts)

Here is the essence of the stackblitz example:

mport { Component } from '@angular/core';

import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  OnInit,
} from '@angular/core';

/** @title Checkboxes with reactive forms */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'checkbox-reactive-forms-example',
  template: `
    <input type="checkbox" [(ngModel)]="checked" /> Normal Checkbox 
    <br>   
    <input type="checkbox" [(ngModel)]="checked" /> Normal Checkbox Linked to samd data
    <br>
    <code>[{{checked?"X":" "}}]</code> State of check variable<br>   
    <mat-checkbox [(ngModel)]="checked">MAT Checkbox not working</mat-checkbox>
    <br>
    <mat-checkbox [(ngModel)]="checked">MAT Checkbox not working</mat-checkbox>
  `,
})
export class CheckboxReactiveFormsExample implements OnInit {
  constructor(private ref: ChangeDetectorRef) {
    ref.detach();
  }
  ngOnInit() {
    // needed for the initial drawing
    this.ref.detectChanges();
  }
  private _checked = true;
  get checked() {
    return this._checked;
  }
  set checked(c) {
    this._checked = c;
    this.ref.detectChanges();
  }
}

Expected Behavior

The checkbox should reflect the state of the model after a call to detectChanges()

Actual Behavior

The checkbox is out of sync with the model.

Environment

scharf commented 2 years ago

I have updated the stackblitz with a hack to solve the problem: detectChanges() has to be called a second (in a setTimeput) to update the component correctly