使用FormControl.setValue()

时间:2018-10-28 15:56:43

标签: angular angular-reactive-forms

考虑以下反应式设置:

让我们拥有实现SimpleInputComponent接口的自定义ControlValueAccessor。将其添加到由ngIf指令包装的应用模板中,以便我们可以在SimpleInputComponent表达式更改时重新调用ngIf的生命周期。

由于SimpleInputComponent是通过FormControl指令绑定到formControlName实例的,因此您可以通过调用FormControl.setValue方法来更改其值。

我希望FormControl.setValue恰好触发一次SimpleInputComponent.writeValue。不幸的是,这是不正确的。

FormControl.setValue调用SimpleInputComponent.writeValue生命周期的次数是SimpleInputComponent的次数。

现在要模拟问题,您至少需要两个组件:

SimpleInputComponent:

import { Component, forwardRef, Input } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControlName } from '@angular/forms';

@Component({
  selector: 'simple-input-text',
  template: `
    <input [name]="formControlName" type="text" 
      [(ngModel)]="value"
      (input)="onChange($event)"
      (blur)="onBlur()" />
  `,
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SimpleInputComponent), multi: true }
  ]
})
export class SimpleInputComponent implements ControlValueAccessor {
  private value: string;

  constructor() { }

  propagateChange: any = () => {};
  propagateTouch: any = () => {};

  onBlur() {
    this.propagateTouch();
  }
  onChange(event) {
    this.propagateChange(event.target.value);
  }

  writeValue(value: any): void {
    console.log('writeValue() called');
    this.value = value;
  }
  registerOnChange(fn: any): void {
    this.propagateChange = fn;
  }
  registerOnTouched(fn: () => void): void {
    this.propagateTouch = fn;
  }
  setDisabledState?(isDisabled: boolean): void {}
}

AppComponent:

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
  selector: 'my-app',
  template: `
    <div>
      <button (click)="hidden = !hidden">{{hidden ? 'show' : 'hide'}}</button>
      <button (click)="changeValue()">change value</button>
    </div>

    <div [formGroup] = "form" *ngIf="!hidden">
      <simple-input-text formControlName="test"></simple-input-text>
    </div>
  `
})
export class AppComponent  {
  hidden = false;

  form = new FormGroup({
    test: new FormControl('')
  });

  changeValue(): void {
    console.log('changeValue() called');
    this.form.get('test').setValue('another value');
  }
}

AppComponent包含2个按钮:

  1. 显示/隐藏-重新调用SimpleInputComponent的生命周期
  2. 更改值-通过实施自己的FormControl.setValue方法来运行SimpleInputComponent更新writeValue

模拟场景:

  • 单击“显示/隐藏”按钮(2n)次
  • 打开控制台,单击“更改值”按钮
  • 检查控制台,看是否调用了writeValue(n + 1)次(其中1次来自初始生命周期)

实时示例位于https://stackblitz.com/edit/angular-bgp3kg

所以问题是:我做错了吗?

1 个答案:

答案 0 :(得分:0)

我看到此修复程序的PR仍处于打开状态。您可以在这里查看。 https://github.com/angular/angular/pull/29335

表示要变通,您可以在SimpleInputComponent中定义ngOnDestroy,如下所示,

ngOnDestroy() {
    if (this.ngControl) {
      this.ngControl.reset();
      this.ngControl.valueAccessor &&
        (this.ngControl.valueAccessor.writeValue = () => {});
    }
  }