16

Refering to : https://angular.io/docs/ts/latest/api/forms/index/FormArrayName-directive.html, it is easy to get a FormArrayName :

<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div formArrayName="cities">
    <div *ngFor="let city of cities.controls; index as i">
       <input [formControlName]="i" placeholder="City">
    </div>
  </div>
  <button>Submit</button>
</form>
form = new FormGroup({
  cities: new FormArray([
    new FormControl('SF'),
    new FormControl('NY')
  ])
});

get cities(): FormArray { return this.form.get('cities') as FormArray; } 
// This does the magic!

The DOM <div formArrayName="cities"> uses the getter get cities(): FormArray { return this.form.get('cities') as FormArray; } and everything is working like a charm

BUT

How to make the getter when the FormArray is nested in another FormArray?

Let's say this example :

form = new FormGroup({
  cities: new FormArray([
    new FormGroup({ 
      name: new FormControl('SF'),
      sisterCities: new FormArray(['Shanghai','Zurich',...])
    }),
    new FormGroup({
      name: new FormControl('NY'),
      sisterCities: new FormArray(['London','Oslo',...])
    }),
  ]),
});

get cities(): FormArray { return this.form.get('cities') as FormArray; } 
// still get the main cities FormArray

// but 
// get sisterCities() won't work because I need to target a city FormGroup (NY or SF) before accessing its sisterCities FormArray.
// and AFAIK, it is not possible to pass parameters to a getter.
<form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div formArrayName="cities">
    <div *ngFor="let city of cities.controls; index as i" [formGroupName]="i">
       <input [formControlName]="name" placeholder="City">
       <div formArrayName="sisterCities"> <!-- this will never work -->
         <div *ngFor="let sisterCity of sisteCities.controls; index as j">
           ...
         </div>
       </div>
    </div>
  </div>
  <button>Submit</button>
</form>

Please, help me to achieve this. Thank you in advance.

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
M'sieur Toph'
  • 2,534
  • 1
  • 21
  • 34
  • I have found a workaround : I do not use a FormNameArray neither a getter for the sisterCities, and use `*ngFor="let sisterCity of city.controls.sisterCities.controls` for my nested ngFor instead, but it does seem a bit ugly to me ... – M'sieur Toph' Jun 08 '17 at 06:56
  • 2
    another good solution https://stackoverflow.com/a/48527939/9583544 . Works like charm – Nikita Mishin Aug 27 '18 at 10:31

2 Answers2

19

I was struggling with the same problem. And finally solved it. Firstly we looking to main form array 'cities' structure.

Nested FormArray structure

Which is the yellow highlighted controls at image is the first array control path. => cities And green highlighted control is the second array control. => sisterCities

    <form [formGroup]="form" (ngSubmit)="onSubmit()">
  <div formArrayName="cities">
    <div *ngFor="let city of cities.controls; index as i" [formGroupName]="i">
       <input [formControlName]="name" placeholder="City">
       <div formArrayName="sisterCities"> <!-- this will never work -->
         <div *ngFor="let sisterCity of cities.controls[i]sisterCities.controls; index as j">
           ...
         </div>
       </div>
    </div>
  </div>
  <button>Submit</button>
</form>

Proper way to access this second nested FormArray is accessing first control array after insert current cities index. And respectively sisterCities,control.

let sisterCity of cities.controls[i].sisterCities.controls
Murat Çimen
  • 465
  • 4
  • 8
  • Thx. I found the same workaround (as I explained in my comment). By the way, I think you are missing a dot in `cities.controls[i]sisterCities.controls` ==> `cities.controls[i].sisterCities.controls` – M'sieur Toph' Oct 25 '17 at 06:11
  • Yeah i forgot dot. I hope helps anyone this apporach. – Murat Çimen Oct 25 '17 at 18:07
  • 1
    I got an error "Property 'sisterCities' does not exist on type 'AbstractControl'." But this way is working: `*ngFor="let sisterCity of cities.get('sisterCities').controls; let i=index"` – Experimenter Jul 01 '21 at 16:13
  • @Experimenter this answer written in 2017. Angular continuously evolving. get function gets you AbstractControl, I guess what we want here. – Murat Çimen Jul 02 '21 at 07:49
3

it worked like a charm for me same as nested form groups .. you need to:

  1. use [formGroupName]="i"
  2. formControlName cannot be dynamic .. instead it should have the name that you when you initialized the from group and there are countless example for that.
  3. you need to iterate over the form array controls in the same div that you used formArrayName attribute in.