2

I am trying to create a verify password validation for reactive form in angular, but keeps giving back a Cannot read property 'get' of undefined error in the console, I can't figure out whats wrong

I have tried accessing password field in various ways but just get difference types of can't access, doesn't exist errors

here is the Ts code:

import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { NgForm, FormsModule, FormGroup, FormControl, Validators } from '@angular/forms';
import { ApartmentService } from '../apartments/apartment.service';
import { Apartment } from '../apartments/apartment.model';

@Component({
  selector: 'app-signup',
  templateUrl: './signup.component.html',
  styleUrls: ['./signup.component.css']
})
export class SignupComponent implements OnInit, AfterViewInit {
  @ViewChild('terms') terms: ElementRef ;
  
  signupForm: FormGroup;

  constructor(private apartmentService: ApartmentService) { }
  date: Date = new Date();  

  // Used to set maxdate to current date
  maxDate = (new Date().getFullYear()).toString()+"-0"+(new Date().getMonth()+1).toString()+"-"+(new Date().getDate()).toString();
  
  ngOnInit() {
    this.signupForm = new FormGroup({
      'firstName': new FormControl(null, Validators.required),
      'lastName': new FormControl(null, Validators.required),
      'email': new FormControl(null, [Validators.required, Validators.email]),
      'passwordFields': new FormGroup({
        'password': new FormControl(null, Validators.required),
        'verifyPassword': new FormControl(null, [Validators.required, this.passwordMatchFail.bind(this)]),
      }),
      'birthdate': new FormControl(null),
      'flatPurchaceDate': new FormControl(null, Validators.required),
      'profilePicture': new FormControl(null),
      'flatDetails': new FormGroup({
        'flatBlock': new FormControl(null, Validators.required),
        'flatNumber': new FormControl(null, Validators.required)
      }),
      'mobileNumber': new FormControl(null, Validators.required),
      'terms': new FormControl(null)
    });
  }

  // terms validation
  ngAfterViewInit() {
    var termsAccepted = this.terms.nativeElement.checked;
    console.log(termsAccepted);
  }
  
  onSubmit(form: NgForm) {
    console.log(form)
    const firstName: string = form.value.firstName;
    const lastName: string = form.value.lastName;
    const flatBlock: string = form.value.flatDetails.flatBlock;
    const flatNumber: string = form.value.flatDetails.flatNumber;
    const email: string = form.value.email;
    
    var newApartment = {
      fBlock: flatBlock,
      fNumber: flatNumber,
      oName: firstName
    }
    this.apartmentService.addApartment(newApartment)
    console.log("Apt ADd")
    console.log(newApartment);
  }
  

  passwordMatchFail(control: FormControl): {[s: string]: boolean} {
    if(this.signupForm.get('passwordFields.password').value !== control.value) {
      return {'passwordMatchFail': true};
    }
    return null;
  }
}

The Html code:

<div class="row main-c">
  <div class="col-xs-8 col-xs-offset-2">
    
    
    <form [formGroup]="signupForm" (ngSubmit)="onSubmit()" >      
      <div class="row">
        <div class="col-xs-6">

          <!-- FIRST NAME -->
          <div class="form-group">
            <input 
            placeholder="First Name"
            required
            formControlName="firstName"
            pattern="[a-zA-Z]+"
            maxlength="50"
            class="form-control"
            type="text" name="firstName" id="fname">
          </div>
          <span class="help-block"
          *ngIf="!signupForm.get('firstName').valid && signupForm.get('firstName').touched"
          >This field is required</span>
        </div>

        <!-- LAST NAME -->
        <div class="col-xs-6">
          <div class="form-group">
            <input 
            placeholder="Last Name"
            formControlName="lastName"
            required
            pattern="[a-zA-Z]+"
            maxlength="50"
            class="form-control"
            type="text" name="lastName" id="lastName">
          </div>
          <span class="help-block"
          *ngIf="!signupForm.get('lastName').valid && signupForm.get('lastName').touched"
          >This field is required</span>
        </div>
      </div>
      
      <!-- EMAIL -->
      <div class="form-group">
        <input 
        placeholder="[email protected]"
        formControlName="email"        
        required
        email
        class="form-control"
        type="email" name="email" id="email">
      </div>
      <span class="help-block"
      *ngIf="!signupForm.get('email').valid && signupForm.get('email').touched"
      >Please enter a valid email</span>
      
      <!-- PASSWORD FIELDS -->

      <div
      formGroupName="passwordFields"
      class="password-fields">
        <!-- PASSWORD -->
      <div class="form-group">
        <input
        formControlName="password"        
        placeholder="Password"
        required
        pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}"
        class="form-control"
        type="password" name="password" id="password">
      </div>
      <span class="help-block"
      *ngIf="!signupForm.passwordFields.get('password').valid && signupForm.passwordFields.get('password').touched"
      >Please enter a valid password, password must be 8 characters long and contain 1 number, 1 special charactrer and 1 uppercase letter.</span>
      
      <!-- CONFIRM PASSWORD -->
      <div class="form-group">
        <input 
        placeholder="Confirm Password"
        formControlName="verifyPassword"        
        class="form-control"
        type="password" name="verifyPassword" id="verifyPassword">
      </div>
      <span class="help-block"
      *ngIf="!signupForm.passwordFields.get('verifyPassword').valid && signupForm.passwordFields.get('verifyPassword').touched"
      >Passwords do not match</span>
      </div>
      
      
      <!-- BIRTHDAY -->
      <div class="row">
        <div class="col-xs-6">
          <div class="form-group">
            <input 
            formControlName="birthdate"
            [max]="maxDate"
            class="form-control"
            placeholder="Birth Date"
            type="date" name="birthdate" id="birthdate">
          </div>
        </div>

        <!-- FLAT PURCHACE DATE -->
        <div class="col-xs-6">
          <div class="form-group">
            <input 
            required
            formControlName="flatPurchaceDate"
            placeholder="Flat Purchace date"
            [max]="maxDate"
            class="form-control date"
            type="date" name="flatPurchaceDate" id="flatPurchaceDate">
          </div>
          <span class="help-block"
          *ngIf="!signupForm.get('flatPurchaceDate').valid && signupForm.get('flatPurchaceDate').touched"
          >Please select a purchace date</span>

          
        </div>
        
      </div>
      <!-- PROFILE PICTURE -->
      <div class="form-group">
        <label for="proficePicture">Profile Picture</label>
        <input 
        formControlName="profilePicture"
        class="form-control-file"
        size="60"
        type="file" name="profilePicture" id="profilePicture" >
      </div>
      
      <!-- FLAT DETAILS -->
      <div class="flat-details" 
      formGroupName="flatDetails"
      >
      <!-- Flat Block -->
          <div class="row">
              <div class="col-xs-2">
                <div class="form-group">
                  <label for="flatBlock">Flat Block</label>
                  <select 
                  required
                  class="form-control"
                  id="flatBlock" name="flatBlock">
                  <option selected value="A">A</option>
                  <option value="B">B</option>
                </select> 
              </div>
            </div>
      <!-- Flat Number -->
            <div class="col-xs-2">
              <div class="form-group">
                <label for="flatNumber">Flat Number</label>
                <input 
                required
                placeholder="1"
                min="1" max="10"
                class="form-control"
                type="number" name="flatNumber" id="flatNumber">
              </div>
            </div>
      
          </div>
      </div>
      <!-- Help Block -->
      <!-- EDIT -->
    <span class="help-block"
        *ngIf="!signupForm.get('flatDetails').valid && signupForm.get('flatDetails').touched"
        >Please choose a valid flat block and number</span> 
      
    
    <!-- MOBILE NUMBER -->
    <div class="form-group">
      <label for="mobileNumber">Mobile Number</label>
      <input 
      required
      pattern="[1-9]{1}[0-9]{11}"
      placeholder="12 Digits Including Country code"
      maxlength="12"
      class="form-control"
      type="tel" name="mobileNumber" id="mobileNumber">
    </div>
    <span class="help-block"
        *ngIf="!signupForm.get('mobileNumber').valid && signupForm.get('mobileNumber').touched"
        >Please enter a valid 12 digit contact number</span>
    
        <!-- TERMA AGREEMENT -->
    <div class="form-group">
      <div class="">
        <input
        required
        class=""
        type="checkbox" #terms id="terms" name="terms">
        <label id="checkbox-label" for="terms" class="">I agree to terms and Conditions </label>          
      </div>
    </div>
    
    <!-- terms accepted to allow t&c checked requirement -->
    <button [disabled]="!signupForm.valid && !termsAccepted" class="btn btn-primary">Sign Up</button>
  </form>
  <br>
  <a routerLink="">Forgot Password</a>

  
  <br><br><br><br><br>

</div>
</div>

2
  • 1
    Can you reproduce this in stackblitz? Commented Jul 2, 2018 at 6:49
  • Why are you binding the scope? Did you try without binding? Commented Jul 2, 2018 at 7:06

2 Answers 2

4

Your code can be simplified as follows. Use FormBuilder to create ``FormGroup` instance. And add the custom validator to the form group.

In component.ts

import { Component } from '@angular/core';
import {
  FormBuilder,
  FormGroup,
  ValidationErrors,
  ValidatorFn,
  Validators
} from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  public registerForm: FormGroup;


  constructor(private fb: FormBuilder) {
    this.buildRegisterForm();
  }

  private buildRegisterForm(): void {
    this.registerForm = this.fb.group(
      {
        email: [
          null,
          Validators.compose([Validators.required, Validators.email])
        ],
        password: [null, Validators.required],
        confirmPassword: [null, Validators.required]
      },
      { validator: this.passwordValidator() }
    );
  }

  private passwordValidator(): ValidatorFn {
    return (group: FormGroup): ValidationErrors => {
      const password = group.controls['password'];
      const confirmPassword = group.controls['confirmPassword'];
      if (password.value !== confirmPassword.value) {
        confirmPassword.setErrors({ notEquivalent: true });
      } else {
        confirmPassword.setErrors(null);
      }
      return;
    };
  }
}

In component.html

<form class="register-frm frm " [formGroup]="registerForm" (ngSubmit)="onRegisterSubmit()" *ngIf="!isLoginTab">
    <div class="form-group">
        <label for="email2">Email:</label>
        <input type="email" required name="email" autocomplete="email" id="email2" formControlName="email">
    </div>
    <div class="form-group">
        <label for="pass3">Password:</label>
        <input type="password" required name="password" id="pass3" formControlName="password">
    </div>
    <div class="form-group">
        <label for="pass2">Confirm Password:</label>
        <input type="password" required name="password" id="pass2" formControlName="confirmPassword">
        <div *ngIf="registerForm.get('confirmPassword')?.errors?.notEquivalent">
            Passwords do not match
        </div>
    </div>
    <div class="sub-btn">
        <input type="submit" class="btn ui-btn info" value="REGISTER" [disabled]="registerForm.invalid">
    </div>
</form>.

A working exple can be found in this stackblitz.

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

Comments

0

I might be a bit late but I have stumbled upon this problem myself and this solution worked for me. You can get the parent of your control element and then use get function. I don't know why the form group is not accessible inside of a custom validator. Couldn't find any documentation on that on my 3 minute google search

passwordMatchFail(control: FormControl): {[s: string]: boolean} {
  if(control.parent?.get('passwordFields.password')?.value !== control.value)
    return {'passwordMatchFail': true};
  return null;
}

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.