Angular 5 |带有 ControlValueAccessor 的 ReactiveForm | onChange 未触发
Angular 5 | ReactiveForm with ControlValueAccessor | onChange is not triggered
我有一个自定义的 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
.
中注释掉的代码
尝试这样的事情
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
,因此您需要将其导入到您的模块中。
我有一个自定义的 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
.
尝试这样的事情
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
,因此您需要将其导入到您的模块中。