为什么ngModel不会在writevalue方法中触发changedetection?

时间:2017-09-06 08:55:42

标签: javascript angular

我写了一个非常简单的自定义表单控件,我没有更改它的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);
  }
}

然后我在名为ngmodel-demo 的组件中使用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) {}
}

当我运行应用程序时,我发现计数器组件的值为1,但其视图未更新

然后我设置一个计时器来更新ngModel并标记为check。

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

结果是每次计数器组件的视图显示的值是最后一个ngModel的值。

在writeValue方法中手动调用markForCheck有效。但我没有使用onPush策略,我不明白为什么要手动调用?

还有一个难题就是为什么在writeValue中调用onChange也可以。

stackblitz上的在线演示链接:https://stackblitz.com/edit/angular-cfc-writevalue

1 个答案:

答案 0 :(得分:2)

这是Angular中的一个错误。我开了一个关于它的issue。如果感兴趣,您可以订阅。