将由多个 Form Group 组成的 Form Array 动态绑定到反应形式的模板时出错 Angular 12
Errors when dynamically binding Form Array consisting of multiple Form Groups to a template in reactive forms of Angular 12
我的数据由从 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-input 和 lib-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 个对象,我需要显示四组 Full Name、Phone Number 和 Email 控件,它们是 fields[= 的一部分JSON 中的 34=] 数组。如果用户想通过单击按钮添加第 5 个人,我应该为这 3 个控件动态生成一个表单组并显示它,然后在提交表单时我需要 post 所有 5 个对象到 API POST终点。这是我的要求。
我在使用上述模板时遇到错误。他们是
Type 'FormArray' is missing the following properties from type 'FormGroup': registerControl, addControl, removeControl for the line <form *ngIf="form" [formGroup]="form">
Type 'AbstractControl' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 3 more for the line <div [formGroup]="ohFormGroup">
Property 'controls' does not exist on type 'AbstractControl' for the line <ng-container *ngFor="let formField of ohFormGroup.controls;">
我不知道究竟是什么导致了上述问题。请帮我解决这个问题。
根据您想要的 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>
您的 .html
有问题
首先,创建一个 return 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>
是的,因为“丑陋的把戏”使函数成为 return formGroup,但是 Angular 在严格模式下不知道“ohFromGroup”是 FormGroup 还是 FormControl
注意:关于您的 lib-show-errors,formField 应该类似于
[formField]="group(i).get(formField.name)"
我的数据由从 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-input 和 lib-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 个对象,我需要显示四组 Full Name、Phone Number 和 Email 控件,它们是 fields[= 的一部分JSON 中的 34=] 数组。如果用户想通过单击按钮添加第 5 个人,我应该为这 3 个控件动态生成一个表单组并显示它,然后在提交表单时我需要 post 所有 5 个对象到 API POST终点。这是我的要求。
我在使用上述模板时遇到错误。他们是
Type 'FormArray' is missing the following properties from type 'FormGroup': registerControl, addControl, removeControl for the line <form *ngIf="form" [formGroup]="form">
Type 'AbstractControl' is missing the following properties from type 'FormGroup': controls, registerControl, addControl, removeControl, and 3 more for the line <div [formGroup]="ohFormGroup">
Property 'controls' does not exist on type 'AbstractControl' for the line <ng-container *ngFor="let formField of ohFormGroup.controls;">
我不知道究竟是什么导致了上述问题。请帮我解决这个问题。
根据您想要的 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>
您的 .html
有问题首先,创建一个 return 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>
是的,因为“丑陋的把戏”使函数成为 return formGroup,但是 Angular 在严格模式下不知道“ohFromGroup”是 FormGroup 还是 FormControl
注意:关于您的 lib-show-errors,formField 应该类似于
[formField]="group(i).get(formField.name)"