We have a form that takes a service response in order to populate a table (our FormArray) with the correct field names. Given the correct form field names, we're trying to populate the fields for some parameters with another service call to a different database.
A simple example:
ngOnInit() {
this.myForm = this._fb.group({
name: [''],
myArray: this._fb.array([])
}),
initArray();
}
private initArray(){
this.arrayValsService.getValues.subscribe(
response => {
for (item of response) {
let arrayItemGroup = this.createItemGroup(item.code, item.value);
const control = <FormArray>this.myForm.controls['myArray'];
control.push(arrayItemGroup);
}
},
err => {console.error(err);}
};
}
private createItemGroup(code: string, value: string) {
return this._fb_group({
selected: [false],
itemCode: [code],
itemValue: [value],
otherVal1: [''],
otherVal2: ['']
});
}
private setArray() {
if(this.key !== undefined) {
this.dataService.getData(this.key).subscribe(
response => {
this.myForm.patchValue(response);
for (let resItem of response.itemsSet) {
for (let formItem of <FormArray>this.myForm.controls['myArray']) {
if (formItem.controls.itemCode === resItem.code) { // <== ISSUE HERE
formItem.patchValue(response.itemsSet.resItem);
formItem.controls.selected.setValue(true);
}
}
}
err => {console.error(err);}
);
}
}
I'm receiving an error in the console that we 'Cannot read property 'controls' of undefined,' occurring on the marked line above. I need to reference the individual form control groups within the array in order to update their values accordingly. Instead of using the nested for loop, I also tried to use array.find in order to check the specific control within the array element group:
const control = <FormArray>(this.myForm.controls['myArray'])
.find(item => myArray.controls.item.itemCode === resItem.code)
This is also not working; encountering the same issue with controls being a property of undefined. The bottom line is, I need a way to reference the child elements of a formArray and the controls of each of those children.
The below method works for iterating through the formArray.controls array object, but still produces a typescript compile error:
for (let resItem of response.itemsSet) {
const control = this.myForm.controls['myArray'].controls
.find((item:any) => item.value.itemCode === resItem.code);
if(control) {control.controls.selected.setValue(true); // ...other value updates
The control
is an array object, so .find() is working here. Within the children (of type FormGroup) of ['myArray'].controls
there is another array object, child.value which has the code that I am comparing the service response with. Functionally this is doing the intended job; however, I'm still receiving an error: 'error TS2329: Property 'controls' does not exist on type 'AbstractControl'.'
Final edit for future reference:
Typecasting the FormArray itself and the FormGroup I'm 'finding' was effective in removing the initial typescript errors. Upon doing this, however, the form controls I was updating started throwing the same "property 'control' on type 'AbstractControl'" error. I changed the format for referencing the specific control based on this question.
Final result:
for (let resItem of response.itemsSet) {
const control = (<FormGroup>((<FormArray>(this.myForm.controls['myArray'])).controls
.find((item:any) => item.value.itemCode === resItem.code)));
if(control) {control.controls['selected']setValue(true); // ...other value updates
Key takeaways: typecasting FormGroups, FormArrays; referencing explicitly named controls with ['control'] format.
The formItem
variable that you have is not a FormControl. The solution, then, is to access the controls array on the FormArray directly and iterate:
for (let formItem of <FormArray>this.myForm.controls['myArray'].controls) {
if ((<FormGroup>formItem).controls['itemCode'] === resItem.code) {
<...>
(<FormGroup>formItem).controls['selected'].setValue(true);
}
}
The other way should have a bunch of "undefined" popping up if you log them.
Here's a plunker demonstrating this subtlety: http://plnkr.co/edit/JRbrPR2g2Kl3z6VJV5jQ?p=preview
For your TypeScript errors, if you know better than the compiler then you're going to have to cast.
Here it is for the find version:
(<FormGroup>control).controls['selected'].setValue(true);