[SOLVED] Bind JSON Array to a Nested Form Group

Table of Contents

Issue

I have a JSON object with an array of addresses.
I want the form to display multiple rows of inputs depending on the number of addresses, with the values of the address bound to the inputs.
The simple values are successfully bound to their respective inputs, but it’s not the case for the inputs of the addresses.

As you can see the HTML detects the correct number of nested forms to display but no data is bound to them, and the inputs don’t seem to be connected to the form values whatsoever

{
  "id": 2,
  "name": "John",
  "surname": "Smith",
  "birthday": "12/08/93",
  "address": [
    {
      "id": "2",
      "type": "2",
      "rue": "2",
      "numero": "2",
      "ville": "2",
      "codepst": "2",
      "pays": "2",
      "commentaire": "2"
    },
    {
      "id": "3",
      "type": "3",
      "rue": "3",
      "numero": "3",
      "ville": "3",
      "codepst": "3",
      "pays": "3",
      "commentaire": "3"
    }
  ]
}
export class UpdateFormComponent implements OnInit {

  myForm : FormGroup;
  myPost: any;

  constructor(private fb: FormBuilder) { }

  ngOnInit(): void {
    this.myPost = this.displayPost(this.id)
    this.myForm = this.fb.group({
      name: this.myPost.name,
      surname: this.myPost.surname,
      birthday: this.myPost.birthday,
      address: this.fb.array(this.myPost.address)
    })
    
    
  }
}
<form [formGroup]="myForm">
    Value: {{ myForm.value | json}}
    <hr>
    <mat-form-field>
        <input matInput placeholder="Nom" formControlName="name">
    </mat-form-field>
    
    <mat-form-field>
        <input matInput placeholder="Prénom" formControlName="surname">
    </mat-form-field>
    
    <mat-form-field>
        <input matInput placeholder="Date de naissance" formControlName="birthday">
    </mat-form-field>
    <br>
    <br>
    <div *ngIf="myForm.get('address')['controls'].length > 0" formArrayName="address" >

        <div *ngFor="let address of myForm.get('address')['controls']; let i=index" [formGroupName]="i">
            <mat-form-field>
                <input matInput placeholder="identifiant" formControlName="id">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="type" formControlName="type">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="rue" formControlName="rue">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="numero" formControlName="numero">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="ville" formControlName="ville">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="codepst" formControlName="codepst">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="pays" formControlName="pays">
            </mat-form-field>
            <mat-form-field>
                <input matInput placeholder="commentaire" formControlName="commentaire">
            </mat-form-field>
        </div>
    </div> 
    <br>
</form>

Solution

You should not directly assign the this.myPostaddress value to FormGroup (this.myForm.address). Instead, you need to iterate each element in this.myPost.address to create FormGroup and then add it into FormArray.

<div
  *ngFor="
    let address of myForm.get('address')['controls'];
    let i = index
  "
  [formGroup]="address"
>
  <mat-form-field>
    <input matInput placeholder="identifiant" formControlName="id" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="type" formControlName="type" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="rue" formControlName="rue" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="numero" formControlName="numero" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="ville" formControlName="ville" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="codepst" formControlName="codepst" />
  </mat-form-field>
  <mat-form-field>
    <input matInput placeholder="pays" formControlName="pays" />
  </mat-form-field>
  <mat-form-field>
    <input
      matInput
      placeholder="commentaire"
      formControlName="commentaire"
    />
  </mat-form-field>
</div>
ngOnInit(): void {
  this.myPost = this.displayPost(this.id);

  this.myForm = this.fb.group({
    name: this.myPost.name,
    surname: this.myPost.surname,
    birthday: this.myPost.birthday,
    address: this.fb.array([]),
  });

  for (let address of this.myPost.address) {
    this.addAddressForm(address);
  }
}

addAddressForm(address) {
  const lessonForm = this.fb.group({
    id: [''],
    type: [''],
    rue: [''],
    numero: [''],
    ville: [''],
    codepst: [''],
    pays: [''],
    commentaire: [''],
  });

  lessonForm.patchValue(address);
  this.addresses.push(lessonForm);
}

get addresses() {
  return this.myForm.controls['address'] as FormArray;
}

Sample Solution on StackBlitz


References

Angular FormArray – Complete Guide

Answered By – Yong Shun

Answer Checked By – Dawn Plyler (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *