0

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>
7
  • Could you provide addLanguage() function so we can see what is inside? @GRV Commented Sep 13, 2019 at 8:01
  • check mu answer you need to set the upper parent from the form array 🙂 Commented Sep 13, 2019 at 8:15
  • use a getter and get the controls in your ts file. Your IDE will help you and you'll know your code is typescript safe Commented Sep 13, 2019 at 8:27
  • @HarisHajdarevic Please Check the updated code. Thank You Commented Sep 13, 2019 at 9:07
  • @GRV have you check my answer ?? Commented Sep 13, 2019 at 9:47

4 Answers 4

2

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 🔥🔥

Sign up to request clarification or add additional context in comments.

2 Comments

Thank You for your contribution. It really helped me alot.
Can you help me with this?stackoverflow.com/q/57953622/11004264
1

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">

2 Comments

I have tried your suggestion, but still getting the same type error.
Can you help me with this? stackoverflow.com/q/57953622/11004264
1

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)
}

3 Comments

have you check the others answer 🤔🤔??
Why you are asking this?
what the difference between your answer and others answers ?
1

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

7 Comments

what about the upper parent from the formArray 😐😐
Yeah i thought he posted only a code snippet and not the whole app.component.html. You are right :) he is missing formGroup and formGroupName
but also you answer is correct this was a typo and will cause an error 👍
@StefanoM. I Please Check The full code I have updated. Thank You.
@GRV Check my Edit :)
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.