Can't acces FormControl instance directly. Cannot read property 'invalid' of undefined
Asked Answered
S

7

17

Can not acces it in the same way as in Angular docs, so must grab the FormGroup instance first and find FormControl instance in there.. I wonder why? This example works:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label for="username">Username</label>
    <input 
      type="text"
      name="username"
      class="form-control"
      formControlName="username"
    >
    <div *ngIf="myForm.controls.username.invalid" class="alert alert-danger">
      username is required
    </div>
  </div>

While this throws error (difference between these only in *ngIf statement):

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div class="form-group">
    <label for="username">Username</label>
    <input 
      type="text"
      name="username"
      class="form-control"
      formControlName="username"
    >
    <div *ngIf="username.invalid" class="alert alert-danger">
      username is required
    </div>
  </div>

Cannot read property 'invalid' of undefined

form.component:

import {Component} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';

@Component({
  selector: 'sign-up',
  templateUrl: 'app/sign-up.component.html'
})

export class SignUpComponent {

  myForm = new FormGroup({
    username: new FormControl('username', Validators.required),
    password: new FormControl('', Validators.required),
  });
}
Stetson answered 16/6, 2017 at 12:35 Comment(0)
L
26

It throws error because you don't have a variable called username or password.

In order to solve this, you could either:

  1. Store the control in a component variable:

TS:

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent {
  readonly usernameCtrl = this.formBuilder.control('username', Validators.required);
  readonly passwordCtrl = this.formBuilder.control('', Validators.required);
  readonly formGroup = this.formBuilder.group({
    username: this.usernameCtrl,
    password: this.passwordCtrl
  });

HTML:

<div 
  *ngIf="userNameCtrl.invalid" class="alert alert-danger"
>
  username is required
</div>
  1. Use AbstractControl#get to grab the control:

HTML:

<div 
  *ngIf="formGroup.get('username').invalid" class="alert alert-danger"
>
  username is required
</div>
  1. Use AbstractControl#hasError so you'll be able to specify different messages for each existent validation:

HTML:

<div 
  *ngIf="formGroup.hasError('required', 'username')" class="alert alert-danger"
>
  username is required
</div>

DEMO

Libb answered 17/6, 2017 at 1:20 Comment(2)
Ok... I was expecting something a bit more straightforward.. And does it mean that example from docs is wrong...? angular.io/api/forms/FormGroupDirectiveClapperclaw
@FromZeroToHero No, the docs are correct. There's a get method called first that returns the FormControl first.Libb
H
11

You can solve this issue using a Form Group and defining the corresponding getters in your controller. In order to achieve this goal:

In the controller:

1) Remove the form control variables definition and initialization

usernameCtrl: FormControl;
passwordCtrl: FormControl;
...
this.usernameCtrl = this.formBuilder.control('username',Validators.required);
this.passwordCtrl = this.formBuilder.control('', Validators.required);

2)Change the form group initialization to this

ngOnInit() {
this.myForm = this.formBuilder.group({
  username: ['usename', Validators.required]
  password: ['', Validators.required]
});

}

3) Add the getters

get username() { return this.myForm.get('username'); }
get password() { return this.myForm.get('password'); }

In the template:

1) add a parent div with [formGroup]="MyForm"

<div [formGroup]="myForm">
...
</div>

2) change [formControl]="usernameCtrl" for forcontrolName=username and *ngIf="usernameCtrl.invalid" for *ngIf="username.invalid"

<input type="text"
       name="username"
       class="form-control"
       formControlName="username">
  <div *ngIf="username.invalid" class="alert alert-danger"> 
    username is required
  </div>

3) change [formControl]="passwordCtrl" for forcontrolName=password and *ngIf="passwordCtrl.invalid" for *ngIf="password.invalid" te.

<input type="text"
       name="password"
       class="form-control"
       formControlName="password">
  <div *ngIf="password.invalid" class="alert alert-danger">
    password is required
  </div>

Plunker

Hygienic answered 8/1, 2018 at 11:55 Comment(0)
R
6

For me works:

form.component:

getFormControl(name) {
    return this.Form.get(name);
}

template:

<input 
  type="text"
  name="username"
  class="form-control"
  formControlName="username"
>
<div *ngIf="getFormControl('username').invalid" class="alert alert-danger">
  username is required
</div>
Recti answered 30/11, 2018 at 8:35 Comment(0)
A
1

I had the same problem, I add 'this' to "myForm.controls....".It helped me.

Instead of:

<div *ngIf="myForm.controls.username.invalid" class="alert alert-danger">
      username is required
 </div>

Do:

 <div *ngIf="this.myForm.controls.username.invalid" class="alert alert-danger">
      username is required
 </div>

Hope this helped you.

Azal answered 29/9, 2020 at 9:38 Comment(0)
V
1

In the ts file add:

get username() { return this.myForm.get('username'); }
get password() { return this.myForm.get('password'); } }
Velvavelvet answered 26/10, 2020 at 13:41 Comment(0)
M
1

actually I'm just new in Angular, I'm just using it for a month and also searching for some answers hehe but in your Component add a getter like this:

export class SignUpComponent {

 myForm = new FormGroup({
    username: new FormControl('', Validators.required),
    password: new FormControl('', Validators.required),
  });

get username(){
  return this.myForm.controls['username'];
}

}
Monochromat answered 28/12, 2020 at 1:26 Comment(0)
T
0

Another option is to check if username is defined, replacing the following

HTML:
<div *ngIf="username.invalid" class="alert alert-danger">

with the following, should also work

HTML:
<div *ngIf="username !== undefined && username.invalid" class="alert alert-danger">
Tarrsus answered 18/8, 2022 at 21:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.