Search code examples
angulartypescriptangular-reactive-formsformarray

Bind JSON Array to a Nested Form Group


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