0
votes

I am Creating Dynamic Form, using formArray. But I am getting encountered with the "TypeError: Cannot read property 'controls' of undefined"

enter code here
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, FormArray } from '@angular/forms';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
 export class AppComponent implements OnInit {
 title = 'Trainner Registration Form ';
 registrationForm: FormGroup;

 get LanguagesForm() {
    return this.registrationForm.get('Languages') as FormArray;
 }

 addLanguage() {
  this.LanguagesForm.push(this.fb.control(''));
}


 constructor(private  fb : FormBuilder ) {}
   ngOnInit(){
     this.registrationForm = this.fb.group({
      personalDetails : this.fb.group({
      name: this.fb.group({
        firstName: [''],
        lastName: ['']
      }),
      aboutYours: [''],
      dob: [''],
      // lang: [''],
      Languages: this.fb.array([]),
      wTT: ['']
    })
  });

}


onSubmit() {
  console.log(this.registrationForm.value);
  // this._registerationservice.register(this.registrationForm.value).subscribe(
  //   response => console.log('Success', response),
  //   error => console.log('Error',error)
  // );
}

}

Expected Result: If user Click on the button "Add Language", A new Input Field should be created.

Actual Result: I am Getting "TypeError: Cannot read property 'controls' of undefined"

app.component.html File

<div style="text-align:center">
<h1>Welcome to {{ title }}!</h1>
</div>

<mat-horizontal-stepper >

  <mat-step  [stepControl]="personalDetails">
    <ng-template matStepLabel>Enter Personal Details</ng-template>
    <div formGroupName="personalDetails">
      <div formGroupName="name">
          <div class="form-group">
            <label>First Name : </label>
            <input type="text"  formControlName="firstName" class="form-control"  >
          </div>

          <div class="form-group">
            <label>Last Name : </label>
            <input type="text" formControlName="lastName" class="form-control">
          </div>
      </div>    
      <div class="form-group">
          <label>DOB : </label>
          <input type="date" formControlName="dob" class="form-control">
        </div>

        <div class="form-group">
          <label>About Yourself : </label>
          <textarea formControlName="aboutYours" class="form-control"></textarea>
        </div>

        <div class="form-group">
            <label>Language(s) : </label>
            <button type="button" class="btn btn-secondary btn-sm m-2" (click)="addLanguage()">Add Language</button>
            <!-- <input type="text" formControlName="lang" class="form-control"> -->

            <div formArrayName="Languages">
                <div *ngFor="let lang of langsform.controls; let i =index;">
                    <input type="text" class="form-control" [formControlName]="i">
                </div>
              </div>

          </div>


</mat-horizontal-stepper>
</form>
</div>
4
Could you provide addLanguage() function so we can see what is inside? @GRVHaris Hajdarevic
check mu answer you need to set the upper parent from the form array 🙂Muhammed Albarmavi
use a getter and get the controls in your ts file. Your IDE will help you and you'll know your code is typescript safeAndrew Allen
@HarisHajdarevic Please Check the updated code. Thank YouGRV
@GRV have you check my answer ??Muhammed Albarmavi

4 Answers

2
votes

this happen because you don't set the upper formGroup the the personalDetails so the rey to look fro control with name Languages in the registrationForm controls where the Languages is a controls in personalDetails form group,another thing you have a typo related to LanguagesForm.controls must be 'Languages.controls'

<div class="form-group">
  <label>Language(s) : </label>
  <button type="button" class="btn btn-secondary btn-sm m-2" (click)="addLanguage()">Add Language</button>

  <div [formGroup]="registrationForm"> <!-- 👈 -->
    <div [formGroupName]="'personalDetails'"> <!-- 👈 -->

      <div formArrayName="Languages">
        <div *ngFor="let lag of registrationForm.get('personalDetails').get('Languages').controls; let i =index;">
          <input type="text" class="form-control" [formControlName]="i">
        </div>

      </div>
    </div>
  </div>
</div>

you can use get property to get access to Languages Form like this

  langsform() :FormArray { 
    return this.registrationForm.get('personalDetails').get('Languages') as FormArray 
  }

template

<div [formGroup]="registrationForm">
  <div [formGroupName]="'personalDetails'">

    <div formArrayName="Languages">
      <div *ngFor="let lang of langsform.controls; let i =index;">
          <input type="text" class="form-control" [formControlName]="i">
      </div>
    </div>
  </div>
</div>

demo 🔥🔥

1
votes

I think the problem is that you initialize the registrationForm object inside onInit and at this time the template has already been parsed so you should just add an *ngIf="this.registrationForm" on this div <div formArrayName="Languages">

1
votes

Try this way:

In your HTML:

 <div class="form-group">
      <div formArrayName="languages">
        <label>Languages</label>

        <div class="row">
          <div class="col-8">
            <input type="text" class="form-control" id="languages">
          </div>
          <div class="col-4">
            <button type="button" class="btn btn-primary" (click)="onAddLanguages()">Add</button>
          </div>
        </div>

        <div class="form-group row" *ngFor="let languages of registrationForm.get('languages').controls; let i = index">
          <div class="col-8">
            <input type="text" class="form-control" [formControlName]="i">
          </div>
          <div class="col-4">
            <button type="button" class="btn btn-danger" (click)="onDeleteLanguages(i)">Delete</button>
          </div>
        </div>
      </div>
    </div>

In your TS:

this.registrationForm = new FormGroup({
       'languages': new FormArray([])
});
onAddLanguages() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.registrationForm.get('languages')).push(control)
}
onDeleteLanguages(index) {
(<FormArray>this.registrationForm.get('languages')).removeAt(index)
}
1
votes

You named your FormArray "Languages" and not LanguagesForm.

<div *ngFor="let Languages of registrationForm.controls.personalDetails.controls.Languages.controls; let i =index;">
      <input type="text" class="form-control" [formControlName]="i">
</div>

Edit: I changed the getLangsform and the <mat-step> and <form> tags. https://stackblitz.com/edit/angular-lc8mu1