1

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.

1 Answer 1

1

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);
Sign up to request clarification or add additional context in comments.

2 Comments

Following this pattern led me to a couple of ts errors: Type 'FormArray' is not an array or string; property 'controls' does not exist on type 'AbstractControl'. In your plnkr you haven't cast the ['myArray'].controls as a <FormArray> (and I believe it is of type array). I removed that casting, but still receive the error on type AbstractControl and the method does not produce the expected results. I will edit the question with a method that is functionally adequate but still produces the same error.
Have you tried casting? If you know better than the compiler, you're going to have to tell it. Updated the answer to provide examples.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.