2
votes

Here is the problem:

I have a complex form with nested formgroups, here is a "simplified" structure of what it looks like :

-> MyForm (formGroup)
    -> Whatever01 (formControl - input)
    -> Whatever02 (formControl - input)
    -> WhateverGroup01 (formGroup)
        -> Whatever03 (formControl - input)
        -> Whatever04 (formControl - input)
    -> WhateverArray01 (formArray)
        -> WhateverGroup02 (formGroup)
            -> Whatever05 (formControl - input)
            -> WhateverGroup03 (formGroup)
                -> Whatever06 (formControl - input)
        -> WhateverGroup04 (formGroup)
            -> Whatever07 (formControl - input)
    -> ...

Angular side, it should look somehow like this (not correct/complete, but just to give an idea) :

this.myForm = this.fb.group({
    whatever01: ['',Validators.compose([...])],
    whatever02: ['',Validators.compose([...])],
    whateverGroup01: this._formBuilder.group({
        whatever03: ['',Validators.compose([...])],
        whatever04: ['',Validators.compose([...])]
    }),
    whateverArray01: this._formBuilder.array([
        this._formBuilder.group({
            whatever05: ['',Validators.compose([...])],
            whateverGroup03: this._formBuilder.group({
                whatever06: ['',Validators.compose([...])]
            })
        }),
        this._formBuilder.group({
            whatever07: ['',Validators.compose([...])],
        })
    ])
    ...
});

Things go pretty fine from root myForm item to fetching the formArray, but then come the troubles....

So, I just can't access "whatever05" (and 06 but 05 does NOT work... so...) formControl to bind it to the template ! Here is what the template actually looks like (intentionnaly skipped the part before the array), the interesting part is the [????]="????", this is the problem actually.

<form [formGroup]="myForm" ...>
    <!-- ...skipped... -->
    <div formArrayName="whateverArray01" *ngFor="let item of myForm.controls.whateverGroup01.controls.whateverArray01.controls;let idx = index;" [formGroupName]="idx">
        <div [????]="????">
            <input formControlName="whatever05" ... />
            <div [????]="????">
                <input formControlName="whatever06" ... />
            </div>
        </div>    
    </div>
</form>

The formGroups located in the formArray have all the same structure.

Basically, the problem is that I canNOT access the formControls within the formGroups...

I tried several solutions, I used [formGroupName] , formGroupName (without brackets), [formGroup], formControlName, ... But cannot figure out what I have to use to be able to bind the corresponding formGroup/formControl with the data !

I come to this kind of errors by now :

formGroup expects a FormGroup instance. Please pass one in. 
// This when using [formGroup]="item.whateverGroup02"

Cannot find control with name: 'whatever05'
// When using [formGroup]="item.controls.whateverGroup02"

Cannot find control with path: 'whateverArray01 -> 0 ->'
// This when using [formGroupName]="whateverGroup02"

Thanks for reading / help

Here is a plunker with some code :

http://plnkr.co/edit/wf7fcHMkhIwzmLWLnCF8?p=preview

2
formArrayName needs to be in a wrapping element for formGroupName. - unitario
Also this beast right here myForm.controls.whateverGroup01.controls.whateverArray01.controls can be condensed to myForm.get('whateverGroup.whateverArray'). - unitario
Also I think FormArray needs to have identical nested FormGroups. - unitario
Hi! Thanks for comments, My formGroups inside formArray all have the same structure. This will not be a problem. I dont understand what you mean in your first comment... - Julo0sS
It's not the same in your example above. I mean that you are declaring formArrayName and formGroupName in the same element. Move formArrayName to a higher up element. - unitario

2 Answers

2
votes

The build of your form seems correct to me. You have some trouble in your template, the iteration of your FormArray is incorrect, your FormArray is not inside the FormGroup WhateverGroup01, so the iteration should look like this:

*ngFor="let ctrl of myForm.controls.whateverArray01.controls;

As to the template part of your FormArray, it should look like this:

<div *ngFor="let ctrl of myForm.controls.whateverArray01.controls; let i = index" [formGroupName]="i">
   <input formControlName="whatever05" />
   <div formGroupName="whateverGroup03">
      <input formControlName="whatever06" />
   </div>
</div>

That should solve the issue :)

Here's a

Demo

2
votes

First of all your view has wrong binding:

[formGroupName]="whateverGroup02"

There is no whateverGroup02 property in your component. We should pass string as follows:

formGroupName="whateverGroup02"

Then if you build group like:

this.fb.group({
  x: 1,
  y: {
    z: 2
  }
})

you will get

FormGroup
  -> FormControl with name `x` and value `1`   
  -> FormControl with name `y` and value `{ z: 2 }`

It means if you want to manipulate nested formGroups you have to create their. i.e.

let fGroups = data.map(rs => this.buildFormGroup(rs));
...
buildFormGroup(obj) {
  let formGroup: { [id: string]: AbstractControl; } = {};
  Object.keys(obj).forEach(key => {
    formGroup[key] = typeof obj[key] === 'object' ?
      this.buildFormGroup(obj[key]) : 
      new FormControl(obj[key]);
  });
  return this._formBuilder.group(formGroup);
}

Plunker Example