Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why ngModel don't trigger changedetection in writevalue method?

I wrote a very simple custom form control and I didn't change it's changeDetectionStrategy.

@Component({
  selector: 'counter',
  template: `
    <button (click)="increase($event)">+</button>
    {{counter}}
    <button (click)="decrease($event)">-</button>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CounterComponent),
    multi: true
  }]
})
export class CounterComponent implements OnInit, ControlValueAccessor {
  private counter: number = 0;
  private onChange: (_: any) => void;
  private onTouched: () => void;

  constructor(private _cdr: ChangeDetectorRef) { }

  ngOnInit() { }

  writeValue(value) {
    console.log(`Write value`, value);
    this.counter = value;
    // this._cdr.markForCheck(); // it works

    // Use onChange works too
    // if (this.onChange) {
    //   this.onChange(value);
    // }
  }

  registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }

  registerOnTouched(fn: () => void): void { this.onTouched = fn; }

  increase() {
    this.counter++;
    this.onChange(this.counter);
  }

  decrease() {
    this.counter--;
    this.onChange(this.counter);
  }
}

Then I use it in a component named ngmodel-demo with onPush changeDetectionStrategy.

@Component({
  selector: 'ngmodel-demo',
  template: `
    <h3>NgModel Demo</h3>
    <p>Count: {{count}}</p>
    <counter [(ngModel)]="count"></counter>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NgmodelDemoComponent {
  @Input() name: string;

  public count = 1;

  constructor(private _cdRef: ChangeDetectorRef) {}
}

When I ran the app, I found that the counter component have got the value 1, but its view didn't update.

Then I set a timer to update the ngModel and mark for check.

ngOnInit() {
  setInterval(() => {
    this.count = ++this.count;
    this._cdRef.markForCheck();
  }, 3000);
}

The result is that each time the value that counter component's view shows is the value of the last ngModel's.

Manually calling markForCheck in writeValue method works. But I did not use the onPush strategy, I do not understand why to manually call?

There is also a puzzle that is why calling onChange in writeValue also works.

Online demo link on stackblitz: https://stackblitz.com/edit/angular-cfc-writevalue

like image 813
t1mer Avatar asked Sep 06 '17 08:09

t1mer


1 Answers

It is a bug in Angular. I opened an issue about it. You can subscribe if interested.

like image 155
t1mer Avatar answered Nov 19 '22 08:11

t1mer