如何从 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 表示? 我不完全确定您的担忧。关键是:FormControlname 仅由其父 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 对象中获取输入字段的名称?的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 formControl用于选择多个

Angular2 - FormControl 验证模糊

无法绑定到“formControl”,因为它不是“输入”的已知属性 - Angular2 Material Autocomplete 问题

如何在 Angular 2.0 中使用 formControl 访问 Native HTML Input 元素

包含一个组件作为 FormControl Angular2

Angular 2 - 订阅 FormControl 的 valueChanges 是不是需要取消订阅?