我试图弄清楚如何在反应形式中使用管道,以便输入被强制为货币格式。我已经为此创建了自己的管道,我已经在代码的其他区域进行了测试,因此我知道它可以作为一个简单的管道工作。我的管道名称是'udpCurrency'
我可以在堆栈溢出上找到的最接近的答案就是这个:Using Pipes within ngModel on INPUT Elements in Angular2-View然而这不适合我的情况而且我怀疑它与我的表格是被动的事实有关
以下是所有相关代码:
模板
<form [formGroup]="myForm" #f="ngForm">
<input class="form-control col-md-6"
formControlName="amount"
[ngModel]="f.value.amount | udpCurrency"
(ngModelChange)="f.value.amount=$event"
placeholder="Amount">
</form>
组件
import { Component, OnInit, HostListener } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
export class MyComponent implements OnInit {
myForm: FormGroup;
constructor(
private builder: FormBuilder
) {
this.myForm = builder.group({
amount: ['', Validators.required]
});
}
}
错误:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined: '. Current value: 'undefined: undefined'
这是混合模板驱动形式和反应形式时可能发生的情况。你有两个绑定相互斗争。选择模板驱动或反应形式。如果你想进入反应路线,你可以使用[value]
作为你的管道......
请注意,此管道仅用于在视图中显示所需的输出。
<form [formGroup]="myForm">
<input
[value]="myForm.get('amount').value | udpCurrency"
formControlName="amount"
placeholder="Amount">
</form>
我以为我有这个工作,但事实证明,我错了(并接受了错误的答案)。我只是以一种对我更好的方式重新构建我的逻辑,并在上面的评论中回答了雅各布罗伯茨的关注。这是我的新解决方案:
模板:
<form [formGroup]="myForm">
<input formControlName="amount" placeholder="Amount">
</form>
组件:
import { Component, OnInit, HostListener } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UdpCurrencyMaskPipe } from '../../../_helpers/udp-currency-mask.pipe';
export class MyComponent implements OnInit {
myForm: FormGroup;
constructor(
private builder: FormBuilder,
private currencyMask: UdpCurrencyMaskPipe,
) {
this.myForm = builder.group({
amount: ['', Validators.required]
});
this.myForm.valueChanges.subscribe(val => {
if (typeof val.amount === 'string') {
const maskedVal = this.currencyMask.transform(val.amount);
if (val.amount !== maskedVal) {
this.myForm.patchValue({amount: maskedVal});
}
}
});
}
}
管道:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'udpCurrencyMask'
})
export class UdpCurrencyMaskPipe implements PipeTransform {
amount: any;
transform(value: any, args?: any): any {
let amount = String(value);
const beforePoint = amount.split('.')[0];
let integers = '';
if (typeof beforePoint !== 'undefined') {
integers = beforePoint.replace(/\D+/g, '');
}
const afterPoint = amount.split('.')[1];
let decimals = '';
if (typeof afterPoint !== 'undefined') {
decimals = afterPoint.replace(/\D+/g, '');
}
if (decimals.length > 2) {
decimals = decimals.slice(0, 2);
}
amount = integers;
if (typeof afterPoint === 'string') {
amount += '.';
}
if (decimals.length > 0) {
amount += decimals;
}
return amount;
}
}
现在我在这里学到了几件事。一个是雅各布所说的是真的,另一种方式最初才起作用,但在价值发生变化时不会更新。另一个需要注意的重要事项是,与视图管道相比,我需要一种完全不同类型的管道用于掩模。例如,视图中的管道可能会将此值设置为“100”并将其转换为“$ 100.00”但是您不希望在键入值时发生转换,您只希望在完成键入后发生这种情况。出于这个原因,我创建了我的货币掩码管道,它只删除非数字数字并将小数限制为两个地方。
我打算编写一个自定义控件,但发现通过ngModelChange
覆盖FormControl类中的“onChange”更容易。 emitViewToModelChange: false
在更新逻辑期间至关重要,以避免重复发生的更改事件循环。货币的所有管道都发生在组件中,您不必担心会出现控制台错误。
<input matInput placeholder="Amount"
(ngModelChange)="onChange($event)" formControlName="amount" />
@Component({
providers: [CurrencyPipe]
})
export class MyComponent {
form = new FormGroup({
amount: new FormControl('', { validators: Validators.required, updateOn: 'blur' })
});
constructor(private currPipe:CurrencyPipe) {}
onChange(value:string) {
const ctrl = this.form.get('amount') as FormControl;
if(isNaN(<any>value.charAt(0))) {
const val = coerceNumberProperty(value.slice(1, value.length));
ctrl.setValue(this.currPipe.transform(val), { emitEvent: false, emitViewToModelChange: false });
} else {
ctrl.setValue(this.currPipe.transform(value), { emitEvent: false, emitViewToModelChange: false });
}
}
onSubmit() {
const rawValue = this.form.get('amount').value;
// if you need to strip the '$' from your input's value when you save data
const value = rawValue.slice(1, rawValue.length);
// do whatever you need to with your value
}
}
在不知道您的管道代码的情况下,由于您构建该表单的位置,它可能会抛出错误。
尝试使用Angular的更改检测挂钩在输入解析后设置该值:
export class MyComponent implements OnInit {
myForm: FormGroup;
constructor(private builder: FormBuilder) { }
ngOnInit() {
this.myForm = builder.group({
amount: ['', Validators.required]
});
}
}
这里的其他答案对我来说不合适,但我找到了一种效果很好的方法。您需要在响应式表单valueChanges订阅中应用管道转换,但不发出事件,因此它不会创建递归循环:
this.formGroup.valueChanges.subscribe(form => {
if (form.amount) {
this.formGroup.patchValue({
amount: this.currencyMask.transform(form.amount)
}, {
emitEvent: false
});
}
});
这也要求你的管道“取消格式化”那里的任何东西,这通常就像在管道的转换函数中一样简单:
value = value.replace(/\$+/g, '');