I have implemented a stackblitz where you can create a dynamic form using some configuration. Everything works fine till you use a FormArray
. What's the idea? Basically you can have many fields in your configuration file with different type. For example checkbox
, text
, number
select
etc. In some case you should have a more complex structure and so you should have a FormArray
. In this stackblitz I tried to represent what's going on
So, when I found an array
type I'm going to create a FormArray instead of a simple FormControl
that's my list of fields
public fields: any[] = [
{
type: "text",
name: "firstName",
label: "First Name",
required: true
},
{
type: "text",
name: "lastName",
label: "Last Name",
required: true
},
{
type: "text",
name: "email",
label: "Email",
required: true
},
{
type: "text",
name: "description",
label: "Description",
required: true,
multiLine: true
},
{
type: "dropdown",
name: "country",
label: "Country",
value: "in",
required: true,
options: [{ value: "in", label: "India" }, { value: "us", label: "USA" }]
},
{
type: "array",
name: "users",
label: "Users",
structure: "users.json"
}
];
And here's my dynamic form builder component
export class DynamicFormBuilderComponent implements OnInit {
@Output() onSubmit = new EventEmitter();
@Input() fields: any[] = [];
form: FormGroup;
allDatas: any;
constructor() {}
ngOnInit() {
let fieldsCtrls = {};
this.allDatas = getData();
for (let f of this.fields) {
if (f.type != "array") {
fieldsCtrls[f.name] = new FormControl(
this.allDatas[f.name] || "",
Validators.required
);
} else {
fieldsCtrls[f.name] = new FormArray([
new FormControl(this.allDatas[f.name] || "", Validators.required)
]);
}
}
this.form = new FormGroup(fieldsCtrls);
}
}
As you can see, I set a value from allDatas, if exists, and I create the FormGroup with FormControls and FormArray. But when I generate the arraybox
I have errors. This is the arraybox component that displays a FormArray control:
import { HttpClient } from "@angular/common/http";
import { Component, Input, OnInit } from "@angular/core";
import { FormGroup } from "@angular/forms";
import { ConfigService } from "../../config.service";
@Component({
selector: "arraybox",
template: `
<div [formGroup]="form">
<div formArrayName="{{ field.name }}">
<div
*ngFor="let obj of arrayFileds; index as idx"
[formGroupName]="idx"
>
<input
attr.type="{{ obj.type }}"
class="form-control"
placeholder="{{ obj.name }}"
id="{{ obj.name }}"
name="{{ obj.name }}"
formControlName="{{ idx }}"
/>
</div>
</div>
</div>
`
})
export class ArrayBoxComponent implements OnInit {
@Input() field: any = {};
@Input() form: FormGroup;
arrayFileds: any = [];
get isValid() {
return this.form.controls[this.field.name].valid;
}
get isDirty() {
return this.form.controls[this.field.name].dirty;
}
constructor(private config: ConfigService) {}
ngOnInit(): void {
this.arrayFileds = this.config.getData(this.field.structure);
console.log(this.arrayFileds);
}
}
getData() returns the structure of that FormArray. In this case
[{
"label": "Username",
"name": "userName",
"type": "text"
}, {
"label": "User email",
"name": "userEmail",
"type": "text"
}]
The errors in console are:
ERROR
Error: Cannot find control with path: 'users -> 0 -> 0'
ERROR
Error: Cannot find control with path: 'users -> 1'
I really don't know what's wrong here. How can I use FormArray to show the fields with their values in this form?
NB. stackblitz gets me an error if I use http to parse json so for now I'm using directly an import of the json. Basically, this.field.structure
in this example is not used (but it should be).