angulartypescriptangular-reactive-formsformarrayangular17

Error on creating dynamic form in Angular 17


I want to create a dynamic form that is an array of payments, the user can add a new payment, delete from the array, and edit.

My HTML:

<form [formGroup]="createLoanPaymentsForm" (ngSubmit)="createLoan()">
    <ng-container formArrayName="createLoanPaymentsForm">
        @for (
            createLoanPaymentForm of createLoanPaymentsForm.controls; // here is the error
            track $index
        ) {
            <div [formGroup]="createLoanPaymentForm">
                <mat-form-field appearance="fill">
                    <input matInput formControlName="title" placeholder="Lesson title" />
                </mat-form-field>
            </div>
        }
        <button mat-mini-fab (click)="addPayment()">Add</button>
    </ng-container>
</form>

The configuration of my component:

@Component({
    selector: 'app-create-loan-dialog',
    standalone: true,
    imports: [
        MatInputModule,
        MatButtonModule,
        MatDialogTitle,
        MatDialogContent,
        MatDialogActions,
        MatDialogClose,
        ReactiveFormsModule,
        MatStepperModule,
    ],
    providers: [
        {
            provide: STEPPER_GLOBAL_OPTIONS,
            useValue: { showError: true },
        },
    ],
    templateUrl: './create-loan-dialog.component.html',
})

My FormGroup:

createLoanPaymentsForm: FormGroup = this.formBuilder.group({
    payments: this.formBuilder.array([]),
});

There is an error in my loop, it says:

Type '{ [key: string]: AbstractControl<any, any>; }' must have a 'Symbol.iterator' method that returns an iterator.

The solution for this bug, possible the correct configuration for a FormArray loop in Angular 17


Solution

  • Your Reactive Form (HTML) structure is incorrect:

    1. formArrayName with "payments": formArrayName="payments".

    2. In the @for loop, you need to iterate the payments.controls.

    3. Under the @for loop, generate each FormGroup instance with [formGroupName]="index".

    <form [formGroup]="createLoanPaymentsForm" (ngSubmit)="createLoan()">
      <ng-container formArrayName="payments">
        @for (payment of payments.controls; track payment; let index = $index) {
          <div [formGroupName]="index">
    
           ...
    
          </div>
        }
        <button mat-mini-fab (click)="addPayment()">Add</button>
      </ng-container>
    </form>
    
    get payments() {
      return this.createLoanPaymentsForm.controls['payments'] as FormArray;
    }
    

    Demo @ StackBlitz