【问题标题】:Angular 5 | ReactiveForm with ControlValueAccessor | onChange is not triggered角 5 |带有 ControlValueAccessor 的 ReactiveForm | onChange 没有被触发
【发布时间】:2018-09-26 02:19:56
【问题描述】:

我有一个自定义的 ControlValueAccessor,它只是在输入上附加一个货币符号。

@Component({
  selector: 'app-currency-input',
  templateUrl: './currency-input.component.html',
  styleUrls: ['./currency-input.component.scss'],
  providers: [
    CurrencyPipe,
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CurrencyInputComponent),
      multi: true
    }
  ]
})
export class CurrencyInputComponent implements ControlValueAccessor {

  @Input() class = '';

  currencyValue: string;

  onChange: (value: number) => void;
  onTouched: () => void;

  constructor(
    private currencyPipe: CurrencyPipe
  ) { }

  parseToNumber(currencyString: string) {
    this.onChange(this.currencyPipe.parse(currencyString));
  }

  transformToCurrencyString(value: number): string {
    return this.currencyPipe.transform(value);
  }

  writeValue(value: number): void {
    if (value !== undefined) {
      this.currencyValue = this.transformToCurrencyString(value);
    }
  }

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

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

}

CurrencyPipe 只是将字符串解析为数字并将数字转换为货币字符串(带有本地化的十进制分隔符和货币符号)。


当我尝试像这样使用 ReactiveForms 时:

<app-currency-input
  name="amount"
  class="value-box"
  formControlName="amount"
  required
></app-currency-input>

...然后手动输入不会触发onChange()


我有一个解决方法,我订阅控件的valueChanges,然后做一个

control.patchValue(newValue, { emitModelToViewChange: true })

... 成功触发 ControlValueAccessor 的 onChange(不带选项的patchValue 也会这样做,因为true 是此选项的默认值。我只是想在这里指出罪魁祸首。)

但我很想使用一个内置解决方案,该解决方案无法解决额外需要的检查和至少两个 valueChanges。



一个简化的 Plunker 试用:https://embed.plnkr.co/c4YMw87FiZMpN5Gr8w1f/ 请参阅src/app.ts 中注释掉的代码。

【问题讨论】:

  • onChange: (value: number) =&gt; console.log("value change") 没有被调用?
  • 请发帖./currency-input.component.html
  • 什么是?理想情况下,您只想在此处使用普通表单字段。传统上,除非您已导入库,否则您只能在普通表单字段上使用 formControlName。
  • @ForestG 正确,除非我执行提到的解决方法,否则它不会被调用。

标签: angular angular5 onchange angular-reactive-forms controlvalueaccessor


【解决方案1】:

试试这样的

import { Component, Input, forwardRef } from '@angular/core';
import { CurrencyPipe, } from '@angular/common';
import { ReactiveFormsModule, NG_VALUE_ACCESSOR, FormControl, ControlValueAccessor } from '@angular/forms';
import { Subscription } from 'rxjs/Subscription';

@Component({
  selector: 'currency-input',
  template: `<input [formControl]="formControl" (blur)="onTouched()"/>`,
  styles: [`h1 { font-family: Lato; }`],
  providers: [
    CurrencyPipe,
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CurrencyInputComponent),
      multi: true
    }
  ]
})
export class CurrencyInputComponent implements ControlValueAccessor {
  constructor(private currencyPipe: CurrencyPipe) { }

  private onChange: Function;
  private onTouched: Function;

  formControl = new FormControl('');
  subscription: Subscription;

  ngOnInit() {
    this.subscription = this.formControl.valueChanges
      .subscribe((v) => {
        this.onChange && this.onChange(this.transform(v));
      })
  }

  ngOnDestroy() {
   this.subscription.unsubscribe();
  }

  writeValue(val) {
    this.formControl.setValue(this.transform(val), { emitEvent: false });
  }

  registerOnChange(fn) {
    this.onChange = fn;
  }

  registerOnTouched(fn) {
    this.onTouched = fn;
  }

  private transform(val: string) {
    return this.currencyPipe.transform(val, 'USD')
  }
}

请注意,我使用的是ReactiveFormsModule,因此您需要将其导入您的模块中。

Live demo

【讨论】:

  • 它没有用。未添加货币符号。但我得到了一个 JS 错误:Maximum call stack size exceeded。调用 this.onChange() 将导致 formControl 的 valueChanges 事件被触发。
  • 查看我的编辑。还要确保打开现场演示。
  • 我会接受这是正确的答案,因为它为我指明了正确的方向。在我发表评论后,我将订阅更改为 .subscribe(value =&gt; this.writeValue(value)),它按预期工作。
  • 很高兴你知道了。谢谢:)
  • 你拯救了我的一天! tnx
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 1970-01-01
  • 2012-06-08
  • 1970-01-01
相关资源
最近更新 更多