Angular 2:迭代反应式表单控件

Posted

技术标签:

【中文标题】Angular 2:迭代反应式表单控件【英文标题】:Angular 2: Iterate over reactive form controls 【发布时间】:2017-07-03 06:35:03 【问题描述】:

我想markAsDirtyFormGroup 内的所有控件。

【问题讨论】:

【参考方案1】:

发现Object.keys可以处理这个..

    Object.keys(this.form.controls).forEach(key => 
      this.form.get(key).markAsDirty();
    );

对于 Angular 8+,请使用以下内容(基于 Michelangelo 的回答):

    Object.keys(this.form.controls).forEach(key => 
      this.form.controls[key].markAsDirty();
    );

【讨论】:

当我在 onSubmit 中使用此功能时,我收到错误 Cannot invoke an expression whose type lacks a call signature. Type 'AbstractControl' has no compatible call signatures. 有人知道为什么吗? Object.keys(this.registerForm.controls).forEach(key => this.registerForm.controls[key].markAsDirty(); ); 当我尝试 Object.keys 甚至“for in”时,我什么也得不到。然而,如果我 console.log(form.controls) 我可以看到对象中包含的所有各种表单控件。我很困惑。 使用 Angular 5,markAsDirty() / markAsTouched() 不会递归到任何子 FormGroups。我将上面的代码分解为一个递归函数,并在任何子 FormGroups 上调用它。与当前的 Angular Material UI 项目一起工作得更好,以防用户从未接触过所需的元素,当用户尝试提交表单以在该点标记任何元素时,我会调用它。 感谢您阅读我的帖子并更新您自己的答案。官方文档也已经过时了,所以我不得不通过打印每一行来解决这个问题......【参考方案2】:

对于它的价值,还有另一种方法可以做到这一点,而无需使用 Object.keys(...) 魔法:

for (const field in this.form.controls)  // 'field' is a string

  const control = this.form.get(field); // 'control' is a FormControl  


【讨论】:

如何获取循环的索引? 对于那些使用 TSLint 的人,代码可以工作,但 TSLint 抱怨“for (... in ...) 语句必须用 if 语句 (forin) 过滤”。 tslint 指出,引用自 for...in 语句的 javascript 文档 ***.com/questions/40770425/…【参考方案3】:

接受的答案对于平面结构是正确的,但不能完全回答原始问题。一个网页可能需要嵌套的 FormGroups 和 FormArrays,我们必须考虑到这一点来创建一个强大的解决方案。

public markControlsDirty(group: FormGroup | FormArray): void 
    Object.keys(group.controls).forEach((key: string) => 
        const abstractControl = group.controls[key];

        if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) 
            this.markControlsDirty(abstractControl);
         else 
            abstractControl.markAsDirty();
        
    );

【讨论】:

instanceof 在被 Typescript 转译后会一直工作吗? @the-notable instanceof 不是 TypeScript 特定的关键字 (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…) class 数据类型也不是。【参考方案4】:

似乎get 函数不再适用于在 Angular 8 中检索表单中的特定值,所以这就是我根据@Liviu Ilea 的回答解决它的方法。

for (const field in this.myForm.controls)  // 'field' is a string
  console.log(this.myForm.controls[field].value);

【讨论】:

你确定吗? API 文档已经有抽象控制的 get 方法(angular.io/api/forms/AbstractControl#get)。我还没有迁移。现在我很害怕(⊙_◎) @AlanGrosz 是的,我在重写它时也看到了这一点,但即使在控制台中打印所有行时,我也找不到对象上的任何 get 方法。我认为文档落后了。祝你迁移顺利! 我认为他们没有删除它,在 Angular 8 中为我工作。它仍在文档中 angular.io/api/forms/AbstractControl#get【参考方案5】:

使用@Marcos 的答案,我创建了一个可以调用的函数,将 formGroup 作为参数传递,它将每个 formGroup 子控件标记为脏,只是为了使它可以在代码周围的更多地方使用,例如将其放入服务中。

public touchAllFormFields(formGroup: FormGroup): void 
    Object.keys(formGroup.controls).forEach((key) => 
        formGroup.get(key).markAsDirty();
    );

希望对你有所帮助;)

【讨论】:

完美!添加到服务中,以及与 clearValidators、untouch 等类似的功能。可能希望为嵌套控件添加递归检查,但这目前有效。谢谢!【参考方案6】:

    Object.keys( this.registerForm.controls).forEach(key => 
       this.registerForm.controls[key].markAsDirty();
    );

【讨论】:

【参考方案7】:

这对我有用

private markFormGroupTouched(formGroup: FormGroup) 
  Object.keys(formGroup.controls).forEach((key) => 
    const control = formGroup.controls[key];
    control.markAsDirty();
    if ((control instanceof FormGroup)) 
      this.markFormGroupTouched(control);
    
  );

【讨论】:

【参考方案8】:

我创建了这个函数来实现它* 我有一个名为“order”的控件,并将索引传递给他。

"conditionGroups": [
   
     "order": null,
     "conditions": []
   
  ]



updateFormData() 
    const control = <FormArray>this.form.controls['conditionGroups'];  
    control.value.map((x,index)=>
    x.order = index; 
 )

【讨论】:

【参考方案9】:

基于@Keenan Diggs answer,我编写了一个通用函数来遍历平面或嵌套表单,它接受对每个表单控件执行的操作:

export function traverseForm(
    form: FormGroup | FormArray, 
    fn: ((c: AbstractControl, name: string, path: string) => void),
    initialPath: string = '') 
  Object.keys(form.controls).forEach((key: string) => 
    const abstractControl = form.controls[key];
    const path = initialPath ? (initialPath + '.' + key) : key;
    fn(abstractControl, key, path);
    if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) 
        traverseForm(abstractControl, fn, path);
    
  );
 

这样使用:

const markAsDirty = (ctrl: AbstractControl) => 
   if (!(abstractControl instanceof FormGroup) && !(abstractControl instanceof FormArray)) 
      abstractControl.markAsDirty();
   

traverseForm(form, markAsDirty);

【讨论】:

【参考方案10】:

这是我对您问题的解决方案,我正在使用带有索引的 for 循环,希望对您有所帮助。

    for (const key of Object.keys(this.forms.controls)) 
      this.forms.controls[key].markAsDirty();
    

【讨论】:

【参考方案11】:

我正在为一个具有多个具有文件上传的表单的项目寻找类似的解决方案。我需要创建一个表单数据对象并将表单的所有字段复制到多个页面中。这适用于 Angular 11。

const formData : FormData = new FormData();

 Object.keys(this.staffForm.controls).forEach(key => 
      console.log("Control Key => "+key); 
      console.log("Control Val => "+this.staffForm.controls[key].value); 
      formData.append(key, this.staffForm.controls[key].value);
    );

【讨论】:

【参考方案12】:

如果您不想循环访问表单控件,也可以使用这种方式...

export class NewComponent implements OnInit 
  @ViewChild(ClrForm) clrForm: ClrForm;

  form: FormGroup;

  constructor(private formBuilder: FormBuilder) 

  ngOnInit() 
    this.buildForm();
  

  onFormSubmit() 
    if (this.form.invalid) 
      this.clrForm.markAsDirty();
      return;
    
  

  private buildForm() 
    this.form = this.formBuilder.group(
      sender: [null, [Validators.required]],
      sentAt: ['', [Validators.required]]
    );
  

<form clrForm [formGroup]="form" (submit)="onFormSubmit()">
  <div class="clr-form-control clr-row">
    <label for="sender" class="clr-control-label clr-col-4">Sender</label>
    <div class="clr-control-container clr-col-8">
      <app-custom-form-control formControlName="sender"></app-custom-form-control>
    </div>
  </div>

  <clr-date-container>
    <label class="clr-col-4">Data wysłania</label>
    <input
      type="date"
      class="clr-col-8"
      clrDate
      formControlName="sentAt"
    />
  </clr-date-container>

  <input type="submit" value="Save" />
</form>

【讨论】:

【参考方案13】:

您可以使用 formGroup 的 _forEachChild() 方法遍历 FormGroup 的子项。这对我来说适用于修补嵌套 formGroups 中的值。

this.myForm.myFormGroup._forEachChild( control => 
  control.markAsDirty();
)    

【讨论】:

【参考方案14】:

迭代formGroup控件以获取每个表单控件的值的简单解决方案:


formGroup: FormGroup;

this.formGroup = this.formBuilder.group(
      control1: new FormControl('value1'),
      control2: new FormControl(`value2`),
      control3: new FormControl('value3')
    );

Object.keys(this.formGroup.controls).forEach(key => 
      console.log(this.formGroup.controls[key].value)
);

// 输出:

值1

值2

值4

【讨论】:

以上是关于Angular 2:迭代反应式表单控件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 的反应式表单中设置表单控件的值

Angular 反应式表单自定义控件异步验证

验证不会传播到 Angular 中的自定义表单控件 ng-select

Angular 5 - 反应形式

动态禁用控件时显示警告消息 angular2

使用自定义验证和动态值的 Angular 表单