如何从 Angular2 FormControl 对象中获取输入字段的名称?
Posted
技术标签:
【中文标题】如何从 Angular2 FormControl 对象中获取输入字段的名称?【英文标题】:How to get name of input field from Angular2 FormControl object? 【发布时间】:2017-03-14 16:39:43 【问题描述】:我有一个 Angular 2 应用程序,它使用 ReactiveForms
模块来管理使用自定义验证器的表单。验证器收到一个FormControl
对象。当FormControl
被传递给验证器时,我有几个输入字段可以使用相同的自定义验证器。
我在FormControl
上找不到任何公开输入字段名称的方法或公共属性。当然,这很简单,可以看出它的价值。以下显示了我想如何使用它:
public asyncValidator(control: FormControl): [key: string]: any
var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece
return new Promise(resolve =>
this.myService.getValidation(theFieldName, control.value)
.subscribe(
data =>
console.log('Validation success:', data);
resolve(null);
,
err =>
console.log('Validation failure:', err);
resolve(err._body);
);
);
【问题讨论】:
【参考方案1】:你有两个选择:
借助 Attribute
装饰器:
constructor(@Attribute('formControlName') public formControlName)
借助 Input
装饰器:
@Input() formControlName;
要使用这个,你的验证当然需要是一个指令。
【讨论】:
【参考方案2】:我们可以使用 .parent
属性,今天好了["_parent"]
(见下文):
export const getControlName = (control: ng.forms.AbstractControl) =>
var controlName = null;
var parent = control["_parent"];
// only such parent, which is FormGroup, has a dictionary
// with control-names as a key and a form-control as a value
if (parent instanceof ng.forms.FormGroup)
// now we will iterate those keys (i.e. names of controls)
Object.keys(parent.controls).forEach((name) =>
// and compare the passed control and
// a child control of a parent - with provided name (we iterate them all)
if (control === parent.controls[name])
// both are same: control passed to Validator
// and this child - are the same references
controlName = name;
);
// we either found a name or simply return null
return controlName;
现在我们准备调整我们的验证器定义
public asyncValidator(control: FormControl): [key: string]: any
//var theFieldName = control.someMethodOfGettingTheName(); // this is the missing piece
var theFieldName = getControlName(control);
...
.parent
稍后,["_parent"]
现在
目前(今天,现在),当前版本是:
2.1.2 (2016-10-27)
但是关注这个问题:feat(forms): make 'parent' a public property of 'AbstractControl'
这里已经说过了
2.2.0-beta.0 (2016-10-20)
特点
forms:将“parent”设为“AbstractControl”的公共属性 (#11855) (445e592) ...
我们可以稍后将["_parent"]
更改为.parent
【讨论】:
这当然可以让一个人获得对表单的访问权限,但是您如何识别表单中的(许多)字段中的哪一个由传递给验证器的原始FormControl
表示?
我不完全确定您的担忧。关键是:FormControl
的 name
仅由其父 FormGroup
给出。因为只有这样的父级具有controls
的 MAP 和键,代表 名称。而且因为每个控件都有信息,谁是它的.parent
,我们可以在任何地方获取该信息.. 甚至在 validator 内部.. 只需调用上面的 (或调整/优化) 函数getControlName(controlPassedToValidtor)
。换句话说,即使是单个控件,传递给验证器函数就足够了——得到它的名字。唯一的条件是:它必须是FormGroup
的孩子
关注的是对 FormGroup 的访问使我可以访问它包含的每个 FormControl 以及它们所代表的每个字段的名称。但是在我现在可以访问的 FormControl 列表中,我怎么知道 哪个 是传递给验证器的 FormControl。我需要特定字段的名称。除非我遗漏了什么,否则您提供的是所有字段的列表....
嗯,怎么解释。我们在验证器内部有一个控件。我们有一个参考。我们还有一本字典,一组 Name1: control1, Name2: control2, ...
。每个控件 (1, 2, ...) 都是某个 FormControl(或其他 FormGroup 或 FormArray)的一个实例。因此,我们可以将传递给验证器的 control 与它们中的每一个进行比较。如果引用是相同的control === parent.controls[name]
,我们得到了匹配。 这样父子与传递给验证器的控件相同。帮助?要么 ... ?我的意思是,我们必须有一个控制,我们做到了......它已经通过......
我扩展了我的答案,在 cmets 中提供了更多详细信息,并且主要展示了我们如何在验证器内部调用该函数,并接收该控件的名称...希望现在很清楚
【参考方案3】:
不完全是您想要的,但您可以像在某些示例中那样动态创建验证器。
喜欢
typeBasedValidator(controlName: string): ValidatorFn
return(control: AbstractControl): [key: string]: any =>
// Your code using controlName to validate
if(controlName == "something")
doSomething();
else
doSomethingElse();
然后在创建表单时使用验证器,传递控件名称 喜欢
【讨论】:
【参考方案4】:从 Angular 4.2.x 开始,您可以使用公共 parent 属性访问 FormControl
的父 FormGroup
(及其控件):
private formControl: FormControl;
//...
Object.keys(this.formControl.parent.controls).forEach((key: string) =>
// ...
);
【讨论】:
在这里,在 2020 年 5 月,我不得不在上面建议的代码中将 this.formControl.parent.controls 更改为 this.formControl.value。【参考方案5】:扩展 Radim Köhler 的答案。这是编写该函数的更短的方法。
getControlName(c: AbstractControl): string | null
const formGroup = c.parent.controls;
return Object.keys(formGroup).find(name => c === formGroup[name]) || null;
【讨论】:
最简洁的答案,但仍然清楚发生了什么。在 Angular 4 中为我工作。谢谢 构造一个 AbstractControls 映射到名称并在其中查找,而不是生成键列表然后为每次查找搜索键列表不是更好吗? 不起作用:"Element implicitly has an 'any' type because expression of type 'string' can't be used to index type ' [key: string]: AbstractControl; | AbstractControl[]'."
这是非常耗费资源的操作。想象一下表单中的每个控件都迭代表单中的所有组件。渲染形式的复杂度为 O(n²)。但是偶尔在单个字段上这样做是可以的。
@Ionix:使用这个:Object.keys(c.parent.controls).find(name => c === c.parent.get(name))
【参考方案6】:
您可以在验证器中设置控件名称:
this.form = this.fb.group(
controlName: ['',
[
Validators.required,
(c) => this.validate(c, 'controlName')
]
]
);
然后:
validate(c: FormControl, name)
return name === 'controlName' ? invalid: true : null;
【讨论】:
你刚刚让我免于痛苦......我有一个反应形式的异步验证器,它使用控件名称转到服务并获取一些验证信息。但它最初总是在新的 FormControl 位上调用,因此父级未定义,如果您返回 of(null) 以越过未定义的父级,即使下次调用时出现错误,它也会始终将控件设置为 VAILD !【参考方案7】:接受答案的单行变体(也解决了我在评论中提到的错误)。
getName(control: FormControl): string | null
return Object.entries(control.parent?.controls ?? []).find(([_, value]) => value === control)?.[0] ?? null;
【讨论】:
以上是关于如何从 Angular2 FormControl 对象中获取输入字段的名称?的主要内容,如果未能解决你的问题,请参考以下文章
无法绑定到“formControl”,因为它不是“输入”的已知属性 - Angular2 Material Autocomplete 问题