Home > Net >  Angular FormArray
Angular FormArray

Time:05-27

I m not able to access the FormArray in Angular 13.3. its showing this error in console. I have one form group, inside that I have 2 more form groups and 1 form array.

core.mjs:6485 ERROR Error: Cannot find control with name: 'third'

Here is my HTML code:

<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <div [formGroup]="bankFormGroup">

    <input type="text" placeholder="property11" formControlName="property11">

    <div *ngIf="bankFormGroup.get('property11')?.invalid && (bankFormGroup.get('property11')?.dirty || bankFormGroup.get('property11')?.touched)" >

      <div *ngIf="bankFormGroup.get('property11')?.errors?.['required']">
        Required.
      </div>
    </div>

    <div [formGroup]="bankForm2Group">

      <input type="text" placeholder="property21" formControlName="property21">

      <div *ngIf="bankForm2Group.get('property21')?.invalid && (bankForm2Group.get('property21')?.dirty || bankForm2Group.get('property21')?.touched)" >

        <div *ngIf="bankForm2Group.get('property21')?.errors?.['required']">
          Required.
        </div>
      </div>
    </div>

    <ul >

      <li  formArrayName="third" *ngFor="let product of bankForm3Group.controls; let i = index;">

        <div [formGroupName]="i" >

          <div >

            <input type="text" formControlName="property3"  id="property3" placeholder="property3">
          </div>
        </div>
      </li>
    </ul>
  </div>

  <div [formGroup]="bankFormGroup">

    <input type="text" placeholder="property12" formControlName="property12">
  </div>

  <button type="submit">Submit</button>
</form>

TS code:

declare var $: any;
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-first',
  templateUrl: './first.component.html',
  styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {
  form!: FormGroup;

  constructor() {
    this.form = new FormGroup({
      first: new FormGroup({
        property11: new FormControl('property 1.1', Validators.required),
        property12: new FormControl('property 1.2', Validators.required)
      }),

      second: new FormGroup({
        property21: new FormControl('property 2.1', Validators.required)
      }),

      third: new FormArray([
        new FormGroup({
          property3: new FormControl('property 3')
        }),
        new FormGroup({
          property3: new FormControl('property 3')
        }),

      ])
    });
  }

  get bankFormGroup(): FormGroup {
    return this.form?.get('first') as FormGroup;
  }

  get bankForm2Group(): FormGroup {
    return this.form?.get('second') as FormGroup;
  }

  get bankForm3Group(): FormArray {
    return this.form?.get('third') as FormArray;
  }

  //get third(): FormArray {
  //  return this.form?.get('third') as FormArray;

  //}

  onSubmit() {
    console.log('Submit', this.form.value);
  }

  ngOnInit(): void {
    $(".preloader").hide();
  }
}

I have separate FormGroup in TS but in HTML its nested. I create Getter Methods which resolved almost. but I m not able to access FormArray by this. I spent lot of time but no luck. Thanks in advance.

CodePudding user response:

The main problem is that your "divs" are closed wrong. In this stackblitz I ordered the div of your .html.

NOTE 1: You can use formGroupName="first" and formGroupName="second" instead of [formGroup]="bankFormGroup" and [formGroup]="backForm2Group"

NOTE2: You can use the way form.get('formgroupname.formControlName') in your *ngIf,e.g.

<div *ngIf="form.get('second.property21')?.invalid>...</div>

NOTE3:I like more that the formArrayName="third" was in the <ul> instead of the <li> with the *ngFor (but it is a personal opinion)

NOTE4: For me is strange in the .html that you show the inputs "property 1.1" (from the formGroup "first") and "property 1.2" (from the formGroup "second"), and the last input the input "property 1.2" (from the formGroup "first")

CodePudding user response:

Thank you @Eliseo, I have achieved my requirement. I have used <ng-container> to segregate the form group and their elements. By this approach I no longer needed getter methods too. Here is my updated code

HTML:

<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <!--classess removed for clarity-->
  <!--parent div contains first and second group. ng container has been used for each group-->
  <div>

    <!--accessing first group-->
    <ng-container formGroupName="first">
      <input type="text" placeholder="property11" formControlName="property11">

      <div *ngIf="form.get('first.property11')?.invalid && (form.get('first.property11')?.dirty || form.get('first.property11')?.touched)" >

        <div *ngIf="form.get('first.property11')?.errors?.['required']">
          Required.
        </div>
      </div>
    </ng-container>

    <!--classess removed for clarity-->
    <div>

      <!--accessing second group-->
      <ng-container formGroupName="second">
        <input type="text" placeholder="property21" formControlName="property21">

        <div *ngIf="form.get('second.property21')?.invalid && (form.get('second.property21')?.dirty || form.get('second.property21')?.touched)" >

          <div *ngIf="form.get('second.property21')?.errors?.['required']">
            Required.
          </div>
        </div>
      </ng-container>
    </div>

    <ul>
      <!--accessing third array-->
      <ng-container formArrayName="third">
        <li *ngFor="let product of third.controls; let i = index;">

          <div [formGroupName]="i">

            <div>

              <input type="text" formControlName="property3" id="property3" placeholder="property3">
            </div>
          </div>
        </li>
      </ng-container>
    </ul>
  </div>

  <!--classess removed for clarity-->
  <div>
    <!--accessing first group back again-->
    <ng-container formGroupName="first">
      <input type="text" placeholder="property12" formControlName="property12">
    </ng-container>
  </div>

  <button type="submit">Submit</button>
</form>

TS Code:

declare var $: any;
import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-first',
  templateUrl: './first.component.html',
  styleUrls: ['./first.component.css']
})
export class FirstComponent implements OnInit {
  form!: FormGroup;

  constructor() {
    this.form = new FormGroup({
      first: new FormGroup({
        property11: new FormControl('property 1.1', Validators.required),
        property12: new FormControl('property 1.2', Validators.required)
      }),

      second: new FormGroup({
        property21: new FormControl('property 2.1', Validators.required)
      }),

      third: new FormArray([
        new FormGroup({
          property3: new FormControl('property 3')
        }),
        new FormGroup({
          property3: new FormControl('property 3')
        }),

      ])
    });
  }

  get third(): FormArray {
    return this.form?.get('third') as FormArray;
  }

  onSubmit() {
    console.log('Submit', this.form.value);
  }

  ngOnInit(): void {
    $(".preloader").hide();
  }
}

If there is any room of improvement then please let me know. I will update the code.

  • Related