Angular 7 Reactive Forms Cannot read property 'controls' of undefined

13,290

Solution 1

Instead of the convenience method you are using, you could simply reference the field like

[ngClass]="{ 'is-invalid': submitted && loginForm.controls['username'].errors }"

It would then be a case of showing an error message if you so wish using

<div *ngIf="submitted && loginForm.controls['username'].hasError('required')" class="error-message">Username is required</div>

I am not clear on what benefit your current approach is affording you over something straight forward such as this.

Solution 2

Should be pretty easy to fix by checking loginForm in html.

<div class="login__container" *ngIf="loginForm"> <!-- Check here -->
    <div class="login__form">
        <form novalidate [formGroup]="loginForm" (ngSubmit)="onSubmit()">
            <div class="field">
                <p class="control">
                    <input class="input" type="text" placeholder="Username" formControlName="username" [ngClass]="{ 'is-invalid': submitted && f.username.errors }">
                </p>
            </div>
            <div class="field">
                <p class="control">
                    <input class="input" type="password" placeholder="Password" formControlName="password" [ngClass]="{ 'is-invalid': submitted && f.password.errors }">
                </p>
            </div>
            <div class="field">
                <p class="control">
                    <button class="button is-success" [disabled]="!loginForm.valid">
                        Login
                    </button>
                </p>
            </div>
        </form>
    </div>
</div>
Share:
13,290
Harry Blue
Author by

Harry Blue

Updated on June 14, 2022

Comments

  • Harry Blue
    Harry Blue almost 2 years

    I am using ReactiveForms in Angular v7.

    Template

    <div class="login__container">
        <div class="login__form">
            <form novalidate [formGroup]="loginForm" (ngSubmit)="onSubmit()">
                <div class="field">
                    <p class="control">
                        <input class="input" type="text" placeholder="Username" formControlName="username" [ngClass]="{ 'is-invalid': submitted && f.username.errors }">
                    </p>
                </div>
                <div class="field">
                    <p class="control">
                        <input class="input" type="password" placeholder="Password" formControlName="password" [ngClass]="{ 'is-invalid': submitted && f.password.errors }">
                    </p>
                </div>
                <div class="field">
                    <p class="control">
                        <button class="button is-success" [disabled]="!loginForm.valid">
                            Login
                        </button>
                    </p>
                </div>
            </form>
        </div>
    </div>
    

    Component

    import { Component, OnInit } from '@angular/core'
    import { FormBuilder, FormGroup, Validators } from '@angular/forms'
    
    @Component({
        selector: 'app-login-component',
        templateUrl: './login.component.html',
        styleUrls: ['./login.component.scss']
    })
    export class LoginComponent implements OnInit {
        loginForm: FormGroup
        submitted = false
    
        constructor(private fB: FormBuilder) {}
    
        ngOnInit() {
            this.loginForm = this.fB.group({
                username: ['', Validators.required],
                password: ['', Validators.required]
            })
        }
    
        // convenience getter for easy access to form fields
        get f() {
            return this.loginForm.controls
        }
    
        onSubmit() {
            this.submitted = true
    
            // stop here if form is invalid
            if (this.loginForm.invalid) {
                return
            }
    
            console.log(this.loginForm.value)
        }
    }
    

    When I go to this route, I have a console error

    ERROR TypeError: Cannot read property 'controls' of undefined
        at Object.get [as f] (http://localhost:4200/main.js:257:35)
        at http://localhost:4200/vendor.js:92667:9
        at Array.forEach (<anonymous>)
        at deepFreeze (http://localhost:4200/vendor.js:92664:33)
        at http://localhost:4200/vendor.js:92670:7
        at Array.forEach (<anonymous>)
        at deepFreeze (http://localhost:4200/vendor.js:92664:33)
        at http://localhost:4200/vendor.js:92670:7
        at Array.forEach (<anonymous>)
        at deepFreeze (http://localhost:4200/vendor.js:92664:33)
    

    I cannot work out why this happening, the validation around the submit button appears to be working, so I can only assume the fields are being referenced correctly. I suspect the issue is coming from the convenience getter set within the component, however I cannot see what is wrong with this as autocomplete is providing me with the values I expect.

    • Ajay Ojha
      Ajay Ojha over 5 years
      check first 'this.loginForm' is undefined or not in f method. or else you can use loginForm.controls.username.errors in the HTML.
    • shuk
      shuk over 5 years
      Another option is to pass the formBuilder from ngOnInit cycle to constructor cycle
    • Jota.Toledo
      Jota.Toledo over 5 years
      As it is, Im not able to reproduce your issue stackblitz.com/edit/angular-lpstaz
    • Jota.Toledo
      Jota.Toledo over 5 years
      As @OmerShukar mentioned, you could try by instantiating the form in the constructor
    • Harry Blue
      Harry Blue over 5 years
      Using the constructor did not help, it also doesn’t appear to follow the docs set out by Angular.
  • Jota.Toledo
    Jota.Toledo over 5 years
    This wont make a difference