1

As new to Angular(14) I am facing this issue in reactive form while trying to access the controls property of formArray in the template file although I can access other properties like value in the template, so I have a few questions:-

1 Why can I access value property but not control property in the view file and what is the reason behind it?

**The error which I am getting is -> Property 'controls' does not exist on type 'AbstractControl<any, any>'. <ng-container *ngFor="let skill of reactiveForm?.get('skills')?.controls">

After seeing the error message I changed the logic and added a condition that I thought would prevent the issue ->**


    <ng-container *ngIf="reactiveForm?.get('skills')?.hasOwnProperty('controls')">
     <ng-container *ngFor="let skill of reactiveForm?.get('skills')?.controls">
     </ng-container>
    </ng-container>

2 Even after adding the condition why am I getting the same error?

HERE IS FULL CODE.

Ts file ->


    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss'],
    })
    export class AppComponent implements OnInit {
      title = 'reactive-form';
      reactiveForm!: FormGroup;
    
      get skillsControls() {
        return (this.reactiveForm.get('skills') as FormArray).controls;
      }
    
      ngOnInit() {
        this.reactiveForm = new FormGroup({
          // Grouping form
          personalDetails: new FormGroup({
            firstname: new FormControl(null, Validators.required),
            lastname: new FormControl(null, Validators.required),
            email: new FormControl(null, [Validators.required, Validators.email]),
          }),
          gender: new FormControl('male'),
          country: new FormControl('india'),
          hobbies: new FormControl(null),
          skills: new FormArray([
            new FormControl('Cricket'),
            new FormControl('Football'),
            new FormControl('Rugby'),
          ]),
        });
      }
      onSubmit() {
        console.log(this.reactiveForm);
        console.log(this.reactiveForm?.get('skills'));
      }
    }

Template file ->


    <div class="form">
      <h2 id="registration">Ragistration Form</h2>
      <form action="" [formGroup]="reactiveForm" (ngSubmit)="onSubmit()">
    
       <!-- Removed other form fields... -->
    
       <div formArrayName="skills">
          <!-- Able to access value property -->
    
          <!-- <ng-container *ngFor="let skill of reactiveForm.get('skills')?.value">
            <input type="text" placeholder="add skill.." [value]="'skill'">
          </ng-container> -->
    
          <!-- Getting Error -->
          <ng-container *ngIf="reactiveForm?.get('skills')?.hasOwnProperty('controls')">
            <ng-container *ngFor="let skill of reactiveForm?.get('skills')?.controls">
              <input type="text" placeholder="add skill.." [value]="'skill'">
            </ng-container>
          </ng-container>
    
          <!-- Solved issue using getter -->
          <!-- <ng-container *ngFor="let skill of skillsControls; let i = index">
            <input type="text" placeholder="add skill.." [formControlName]="skill">
          </ng-container> -->
        </div>
        <input type="submit" value="Submit" id="btn">
      </form>
    </div>

Wanted to understand the problem and how we could solve this (without using the getter function).

2
  • Any reason why you are not looking for a getter? Commented Sep 2, 2024 at 3:28
  • 1
    Not any specific reason, just curios to know is there any other approach we can use and also whanted to know why it showing error while accessing controls property but not for value property althought both property present in the same object, why different behaviour for different property. Commented Sep 2, 2024 at 7:44

1 Answer 1

1

It's not possible when we use "get" return anything else a AbstractControl

It's true we can use an ugly hack: use $any in the way

<div formArrayName="skills">
  @for (control of $any(reactiveForm.get('skills')).controls;track $index)
  {
  <input [formControlName]="$index">
  }
</div>

or

<div formArrayName="skills">
  <div *ngFor="let control of $any(reactiveForm.get('skills')).controls;
                              let i=index">
         <input [formControlName]="i">
  </div>
</div>

Well, when we have a formArray it's always better that the getter return the formArray itseft (instead create a getter that return the controls). If we have a formArray of formControls return not only a FormArray else a FormArray<FormControl>

Rememeber that a formArray can be a FormArray of FormGroups (the value is an array of object) or a formArray of FormsControls (the value is an array of string/numbers,etc...)

  get skills(){
    return this.reactiveForm.get('skills') as FormArray<FormControl>
  }

this allow us write in .ts

delete(index:number)
{
   this.skills.removeAt(index)
}
//or
add()
{
   this.skills.insert(new FormControl('')
}

Now you can use

    <!--see that if you use formControlName you need enclosed in a 
        div with formArrayName -->
    <div formArrayName="skills">
      @for (control of skills.controls;track $index)
      {
        <!--we use the $index and formControlName-->
      <input [formControlName]="$index">
      }
    </div>

//or

    <!--see that if you use formControl you need NOT enclosed in a 
        div with formArrayName -->
      @for (control of skills.controls;track $index)
      {
          <!--we use the iterate variable "control" and formControl-->
      <input [formControl]="control">
      }

NOTE: Be careful when you define a formControl as new FormControl(null) -use new FormControl(''), see the docs related to strictly typed Fomrs

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.