【问题标题】:Errors when dynamically binding Form Array consisting of multiple Form Groups to a template in reactive forms of Angular 12将由多个表单组组成的表单数组动态绑定到Angular 12的反应形式的模板时出错
【发布时间】:2021-08-26 07:03:56
【问题描述】:

我有由从 API 返回的人员对象数组组成的数据,我必须为此动态生成控件并在 Angular 12 应用程序中显示。返回的数据如下所示。

{
"type": "Person",
"fields": [
    {
        "name": "fullName",
        "label": "Full name",
        "validation": {
            "rules": {
                "required": true,
                "maxLength": 100
            },
            "errorMessages": {
                "required": "Enter your full name",
                "maxLength": "The full name must not exceed 100 characters"
            }
        }
    },
    {
        "name": "phoneNumber",
        "label": "Phone number",
        "validation": {
            "rules": {
                "required": true,
                "maxLength": 16
            },
            "errorMessages": {
                "required": "Enter a valid phone number",
                "maxLength": "The phone number must not exceed 16 characters"
            }
        }
    },
    {
        "name": "email",
        "label": "Email",
        "validation": {
            "rules": {
                "required": true,
                "format": "^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$"
            },
            "errorMessages": {
                "required": "Enter a valid email address",
                "format": "The email address must be valid"
            }
        }
    }
],
"values": [
    {
        "id": 1,
        "fullName": null,
        "phoneNumber": null,
        "email": null
    },
    {
        "id": 2,
        "fullName": "ytrytrytr",
        "phoneNumber": null,
        "email": null
    },
    {
        "id": 3,
        "fullName": "test",
        "phoneNumber": "2353535",
        "email": "dfdw@ww.com"
    }
  ]
}

我的 component.ts 文件中用于获取数据并创建表单数组以及该表单数组中的表单组的代码如下所示。

form!: FormArray;
personFields!: any;
personValues: any;

ngOnInit() {
     this.personService.getData()
         .subscribe((res: FormObject) => {
        this.personFields = res.fields;
        this.personValues = res.values;
    this.form = new FormArray(this.personValues.map((value: 
    any)=>this.createPersonData(value)));
  });

}

createPersonData(person: any) {
    let personFormGroup = new FormGroup({});

    let validationsArray = [];

    this.personFields.forEach(formField => {
      validationsArray = [];
      if(formField.validation.rules.required) {
        validationsArray.push(Validators.required);
      }
      if(formField.validation.rules.maxLength) {
    
  validationsArray.push(Validators.maxLength(formField.validation.rules.maxLength));
      }
      if(formField.validation.rules.format) {
        validationsArray.push(Validators.pattern(formField.validation.rules.format));
      }

      let formFieldValue = person[formField.name] ? person[formField.name] : null;

      personFormGroup.addControl(formField.name, new FormControl(formFieldValue, 
     validationsArray));
    });

  return personFormGroup;
}

现在,我正在绑定到模板 html 文件,如下所示,lib-inputlib-show-errors 是我在 Angular 库中拥有的组件我正在这个应用程序中使用。

<form *ngIf="form" [formGroup]="form">
    <div *ngFor="let ohFormGroup of form.controls;let i=index">
        <div [formGroup]="ohFormGroup">
            <ng-container *ngFor="let formField of ohFormGroup.controls;">
                <div>
                    <div class="label">{{formField.label}}</div>
                    <lib-input [formControlName]="formField.name">
                    </lib-input>
                </div>
                
                <lib-show-errors *ngIf="isSubmitted && ohFormGroup.controls[formField.name].errors" 
                                [formField]="ohFormGroup.controls[formField.name]" 
                                [errorMessages]="formField.validation.errorMessages">
                </lib-show-errors>
            </ng-container>
        </div>
  </div>
</form>

我需要显示对应于 JSON 中 values 数组中返回的人员对象的控件。例如,如果 values 数组有 4 个对象,我需要显示四组全名、电话号码和电子邮件控件,它们是 JSON 中 fields 数组的一部分。如果用户想通过单击按钮添加第五个人,我应该为这 3 个控件动态生成一个表单组并显示它,然后在提交表单时我需要将所有 5 个对象发布到 API POST 端点。这是我的要求。

我在使用上述模板时遇到错误。他们是

“FormArray”类型缺少“FormGroup”类型的以下属性:

行的 registerControl、addControl、removeControl

“AbstractControl”类型缺少“FormGroup”类型的以下属性:controls、registerControl、addControl、removeControl 以及

行的另外 3 个属性

行的“AbstractControl”类型上不存在属性“控件”

我不知道究竟是什么导致了上述问题。请帮我解决这个问题。

【问题讨论】:

  • 您的 JSON 对象必须与您的 FormGroup 的结构完全匹配。现在没有。
  • 我正在从 json 响应动态创建表单数组和表单组,然后绑定到模板。当我记录在控制台中创建的“表单”时,我可以看到表单数组和其中的表单组,但无法将它们绑定到模板。这就是我面临的问题。
  • 我看到您的 JSON 中有数组,但您的代码中没有 FormArrays
  • 在 ngOnInit() 里面我有 this.form = new FormArray(this.personValues.map((value: any)=>this.createPersonData(value)));这是创建 FormArray 的地方。
  • 当您应该使用 FormGroup 时,您正在将 FormArray 传递给您的
    。此外,在访问 FormArray 中的控件时,您需要在 TS 中使用一个方法来对您的数组进行类型转换,否则 Angular 会认为它是一个 AbstractControl(即getFormArray() { return myFormArray as FormArray }

标签: angular angular-reactive-forms formarray formgroups


【解决方案1】:

您的 .html 文件有问题

首先,创建一个返回 FormGroup 的函数

group(index:number)
{
  return this.form.at(index) as FormGroup
}

那你可以-我把代码用简单的输入-

    <div *ngFor="let ohFormGroup of form.controls;let i=index">
            <div [formGroup]="group(i)">
                <!--see that you iterate over "formFiels" not over "ohFormGroup.controls"-->
                <ng-container *ngFor="let formField of personFields">
                    <div>
                        <div class="label">{{formField.label}}</div>
                        <input [formControlName]="formField.name">
                    </div>
                </ng-container>
            </div>
    </div>

是的,就像“丑陋的把戏”一样,让一个函数返回 formGroup,但严格模式下的 Angular 不知道“ohFromGroup”是 FormGroup 还是 FormControl

注意:关于您的 lib-show-errors,formField 应该类似于 [formField]="group(i).get(formField.name)"

【讨论】:

  • 这非常有效。非常感谢:)
【解决方案2】:

根据您所需的 JSON 结构,我将通过以下方式初始化您的表单组:

export class MyComponent extends OnInit {
   @Input()
   person: Person

   formGroup: FormGroup
   ngOnInit() {
      this.formGroup = new FormGroup({
          type: new FormControl(this.person.type),
          fields: new FormArray(
              this.person.fields.map(t => {
                  return this.createField(t)
              }
          ),
          values: new FormArray(
              this.person.values.map(t => {
                  return this.createValue(t)
              })
          )
      })
   }
   createField(field: Field) {
       return new FormGroup({
           name: new FormControl(field.name),
           label: new FormControl(field.label),
           validation: new FormGroup(validation)
       })
   },
   createValue(value: Value) {
       return new FormGroup({
           id: new FormControl(value.id),
           fullName: new FormControl(value.fullName),
           phoneNumber: new FormControl(value.phoneNumber),
           email: new FormControl(value.email)
       })
   }
}

您的模板可能如下所示:

<form *ngIf="formGroup" [formGroup]="formGroup">
    <ng-container *ngIf="formGroup.get('fields'); let fields">
    <div [formArray]="fields">
    <div *ngFor="let field of fields.controls;let i = index">
        <div [formGroupName]="i">
             <div>
                 <div class="label">{{field.label}}</div>
                 <lib-input [formControlName]="field.name">
                 </lib-input>
             </div>
             <lib-show-errors [validation]="field.validation">
             </lib-show-errors>
        </div>
    </div>
    </div>
    </ng-container>
</form>

【讨论】:

  • 感谢您的回复。您的方法将为字段数组和值数组创建不同的控件。我的要求是基于字段数组我需要创建控件并基于值数组我需要分配相应的值。我还有一个添加按钮,用户可以单击该按钮添加一个具有全名、电话号码和电子邮件控件的人,我需要为其动态创建一个 FormGroup 并将其添加到父 FormArray。
  • 我会先决定你理想的 JSON 结构表单组。如果您为 JSON 发布的内容不理想,我会先弄清楚这一点。看起来您正在尝试使用元数据来动态创建表单。如果是这种情况,则无需将这些元字段存储在 FormGroup 中。
  • 我需要显示与 JSON 中的 values 数组中返回的人员对象相对应的控件。如果 values 数组有 4 个对象,我需要显示四组全名、电话号码和电子邮件控件,它们是 JSON 中字段数组的一部分。如果用户想通过单击按钮添加第五个人,我应该为这 3 个控件动态生成一个组并显示它,然后在提交表单时,我需要将所有 5 个对象发布到 API POST 端点。这是我的要求。
  • 您的问题并不清楚。我会修改你的问题。
  • 谢谢。我现在已将我的要求添加到问题中:)
猜你喜欢
  • 2020-01-07
  • 2022-01-03
  • 2020-04-11
  • 2022-01-27
  • 2021-01-12
  • 2021-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多