如何在设置或获取之前截取FormControl的值?
Posted
技术标签:
【中文标题】如何在设置或获取之前截取FormControl的值?【英文标题】:How to intercept the value of FormControl before setting or getting it? 【发布时间】:2018-01-16 16:21:24 【问题描述】:这个问题不言自明。我想拦截 FormControl 的 value 属性的传入值,并能够拦截传出值到它所连接的 html 控件。
假设我有一个名为“firstName”的 FormControl,我将它连接到一个文本框:
<input type="text" formControlName="firstName" />
默认情况下,当用户在文本框中输入值并提交时,FormControl 的值被设置为文本框中的值。有什么方法可以拦截设置的值并在设置之前对其进行修改?
同样,有没有什么办法可以截取FormControl发送给HTML控件的值呢?例如,如果我将 FormControl 的值设置为某个值,但我想修改显示在文本框中的值。
我知道我可以使用 ngModel 作为表单和控件之间的中介,但是当使用多个控件时会变得很麻烦。我也知道您可以创建自己的控件并实现 ControlValueAccessor,但这也很麻烦,因为我必须为要使用的每个控件创建相应的控件。
有关我为什么要问这个问题的更多信息,请参阅https://github.com/ionic-team/ionic/issues/7121
【问题讨论】:
你能创建 plunker 吗?你想输入3
得到777
吗?
基本上,是的。我想要的是用户输入1并将表单控件值设置为0.01。如果表单控件值为 0.01,则文本框应显示 1。
这是一篇关于这个主题的好文章——专门针对 Angular Material。 medium.com/angular-in-depth/… - 还包括一个关于验证的注释,它应该适用于所有控件。
【参考方案1】:
似乎验证器在处理 angular 形式 api 的事件方面具有最高优先级(如果您实际调试到 angular 源代码中,您可以看到);因此,根据您的需要,您可以在表单控件值更改之前使用自定义验证器实际获取用户值。
@Directive(
selector:'[InterceptorDirective]',
providers: [
provide: NG_VALIDATORS, useExisting: forwardRef(() => InterceptorDirective), multi: true
]
) export class InterceptorDirective implements Validator
validate(control: AbstractControl): ValidationErrors | null
let value = control.value
// intercepting
control.setValue(value, emitModelToViewChange: false) // to prevent revalidation we avoid model to view changes
甚至你可以使用指令并在里面注入 Ng Control。因为这是纯粹的指令,你可以在它上面使用@hostlistener(而不是angular调用验证器的validate方法),一切都发生在angular form.js进入处理之前。
@Directive(
selector:'[InterceptorDirective]'
) export class InterceptorDirective implements AfterContentInit
constructor(private control:NgControl)
ngAfterContentInit()
this.control.valueChanges.subscribe(()=>console.log('valueChanges')) // will be logged after hostlistener handler
@HostListener('input')
onInput(event:any)
console.log('input') // will be logged first
第二种方法可以确保验证者也会收到拦截的值
【讨论】:
【参考方案2】:您可以创建一个指令inject the formControl in the directive,然后使用setValue
的emitModelToViewChange
和emitViewToModelChange
选项。
像这样:
@Directive(
selector: '[valueModifier]',
)
export class ValueModifierDirective
constructor(private control: NgControl)
private setValueOfModel()
this.control.control!.setValue('Custom Value', emitModelToViewChange: false );
此解决方案适用于任何类型的表单输入(输入、选择、复选框等)。
【讨论】:
【参考方案3】:你可以编写一个可重用的指令来拦截来自和进入视图的值:
const MODIFIER_CONTROL_VALUE_ACCESSOR: Provider =
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ValueModifierDirective),
multi: true,
;
@Directive(
selector: '[valueModifier]',
host: '(keyup)': 'doSomething($event)' ,
providers: [MODIFIER_CONTROL_VALUE_ACCESSOR],
)
export class ValueModifierDirective implements ControlValueAccessor
@Input() valueModifier: [Function, Function];
private writeToForm;
constructor(public _el: ElementRef)
doSomething(event: Event)
const viewToForm = this.valueModifier[0];
this.writeToForm(viewToForm(event.target.value));
registerOnChange(fn: (value: any) => void)
this.writeToForm = fn;
registerOnTouched(fn: any)
// nothing to do
writeValue(value: any)
const formToView = this.valueModifier[1];
this._el.nativeElement.value = formToView(value);
要使用它,只需将指令添加到您应用 formControlName 的同一元素并传递转换函数:
@Component(
selector: 'my-app',
template: `
<form [formGroup]="form">
<input [valueModifier]="[viewToForm, formToView]" name="value" type="text" formControlName="input" />
<button (click)="random()">Set Random Value</button>
</form>
`,
styleUrls: ['./app.component.css']
)
export class AppComponent
form = new FormGroup(
input: new FormControl(1)
);
viewToForm = (text: string) => "toForm" + text;
formToView = (text: string) => "toView" + text;
constructor()
this.form.valueChanges.subscribe(value => console.log(value));
random ()
this.form.patchValue(
input: Math.random()
)
现场示例(Stackblitz):
https://stackblitz.com/edit/angular-afmkxl?file=src%2Fapp%2Fapp.component.ts
以上适用于文本输入。我认为您可以为其他类型的输入编写类似的指令。
【讨论】:
这是一个很好的解决方案,但它不适用于自定义验证器。如果我错了,请纠正我,但是当我尝试过时,验证器函数没有收到指令的“转换”值。 你能举个例子吗?也许订单已经关闭,所以转换发生在验证之后?【参考方案4】:您可以使用 onBlur 调用函数(即modifyValue()
),然后利用 patchValue 修改值:
<input type="text" onblur="modifyValue()" formControlName="firstName" />
modifyValue()
this.form.patchValue(
firstName: this.form.firstName //modify firstName here
)
如果可行,您可以创建一个通用函数并将键/值传递给以修补它,而无需创建一堆特定函数
<input type="text" onblur="modifyValue('firstName')" formControlName="firstName" />
modifyValue(key)
this.form.controls[key].patchValue(this.form.controls[key] // modify value here)
【讨论】:
如果我使用 onBlur 手动设置表单值,为什么我需要 formControlName="firstName" 呢?同样,问题在于可扩展性。此解决方案适用于小型实例。但是,当您有一个包含许多需要转换的字段的表单时,您最终会得到大量的样板代码。以上是关于如何在设置或获取之前截取FormControl的值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 FormArray 中设置 FormControl 的值
如何从 Angular2 FormControl 对象中获取输入字段的名称?