Add dynamic fields to the form

Viewed 29

I have 2 dynamic input fields and I am trying to add them to my form.

<div formArrayName="details">
  <div *ngFor="let detail of _detailRowNumber; index as i">
    <mat-form-field appearance="fill">
      <mat-label>Label of a detail</mat-label>
      <input id="detail-label" matInput type="text" [formControlName]="i">
    </mat-form-field>

    <mat-form-field appearance="fill">
      <mat-label>Description of a detail</mat-label>
      <input id="detail-description" matInput type="text" [formControlName]="i">
    </mat-form-field>

    <button type="button" *ngIf="_detailRowNumber.length > 1" (click)="decreaseDetailRow(detail)" mat-fab color="primary" aria-label="Remove a new row from the detail list">
      <mat-icon>remove</mat-icon>
    </button>

    <button type="button" (click)="increaseDetailRow(i)" mat-fab color="primary" aria-label="Add a new row to the detail list">
      <mat-icon>add</mat-icon>
    </button>
  </div>
</div>
_detailRowNumber: Array<number> = [0];

details: FormGroup = new FormGroup({
  details: new FormArray([
    new FormControl('label'),
    new FormControl('description')
  ])
})

fields: string[] = [];
formMain = this.fb.group({
  details: this.details
});

increaseDetailRow(index: number): void {
  this._detailRowNumber.splice(++index, 0, Date.now());
}

decreaseDetailRow(index: number): void {
  this._detailRowNumber = this._detailRowNumber.filter((item) => item != index);
}

But I get this error:

ERROR Error: Cannot find control with path: 'details -> 0'

This is what I expect:

{
  details: [
    { label: "My label 1", description: "My description 1" },
    { label: "My label 2", description: "My description 2" }
  ]
}

So how can I achieve my goal?

1 Answers

Your details form control should be the FormArray with FormGroup(s), rather than the FormArray with FormControl(s) in order to achieve the expected result as attached.

<div [formGroup]="formMain">
  <div formArrayName="details">
    <div *ngFor="let detail of _detailRowNumber; index as i">
      <ng-container [formGroupName]="i">
        <mat-form-field appearance="fill">
          <mat-label>Label of a detail</mat-label>
          <input
            id="detail-label"
            matInput
            type="text"
            formControlName="label"
          />
        </mat-form-field>

        <mat-form-field appearance="fill">
          <mat-label>Description of a detail</mat-label>
          <input
            id="detail-description"
            matInput
            type="text"
            formControlName="description"
          />
        </mat-form-field>

        <button
          type="button"
          *ngIf="_detailRowNumber.length > 1"
          (click)="decreaseDetailRow(detail)"
          mat-fab
          color="primary"
          aria-label="Remove a new row from the detail list"
        >
          <mat-icon>remove</mat-icon>
        </button>

        <button
          type="button"
          (click)="increaseDetailRow(i)"
          mat-fab
          color="primary"
          aria-label="Add a new row to the detail list"
        >
          <mat-icon>add</mat-icon>
        </button>
      </ng-container>
    </div>
  </div>
</div>
ngOnInit() {
  this.formMain = this.fb.group({ details: this.fb.array([]) });
  this.addDetailsFormGroup();
}

addDetailsFormGroup() {
  (this.formMain.controls.details as FormArray).push(
    new FormGroup({
      label: new FormControl('label'),
      description: new FormControl('description'),
    })
  );
}

increaseDetailRow(index: number): void {
  this._detailRowNumber.splice(++index, 0, Date.now());
  this.addDetailsFormGroup();
}

decreaseDetailRow(index: number): void {
  this._detailRowNumber = this._detailRowNumber.filter(
    (item) => item != index
  );

  (this.formMain.controls.details as FormArray).removeAt(index);
}

Demo @ StackBlitz

Related