Angular Custom Validation Control doesn't get form value

75 Views Asked by At

This code in my form-validators.ts:

console.log('password:', password?.value);
console.log('confirmPwd:', confirmPwd?.value);

always fetched me undefined, causing the custom validation (matchPwdValidator()) to constantly return false. If I fetched confirmPwd in the switch, it will print out the correct details. Below is a summarized version of my codes.

form-validators.ts

import { AbstractControl, ValidatorFn, Validators } from "@angular/forms";

export class FormValidators {
  ...

  static matchPwdValidator(): ValidatorFn {
    return (control: AbstractControl): { [key: string]: any } | null => {
      const password = control.get("password");
      const confirmPwd = control.get("confirmPwd");
  
      console.log('password:', password?.value);
      console.log('confirmPwd:', confirmPwd?.value);
  
      if (!password || !confirmPwd || password.value !== confirmPwd.value) {
        return { PwdNotMatched: true };
      }
  
      return null;
    };
  }
}

form.component.ts

import { Component } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { FormValidators } from "../utilities/form-validators";

@Component({
  selector: "app-form",
  templateUrl: "./form.component.html",
  styleUrls: ["./form.component.scss"],
})
export class FormComponent {
  
  cpwdError: boolean = false;

  sampleForm: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
  ) {
    
    this.sampleForm= this.formBuilder.group(
      {
        ...
        password: ["", FormValidators.passwordValidator()],
        confirmPwd: ["", FormValidators.matchPwdValidator()],
      },
    );
    
    ...
    this.sampleForm.get('confirmPwd')?.statusChanges .subscribe(() => {
      this.updateErrorFlags('confirmPwd');
    });
  }


  private updateErrorFlags(controlName: string): void {
    const control = this.sampleForm.get(controlName);
    if (control) {
      switch (controlName) {
        ...
        case 'confirmPwd':
          this.cpwdError = control.hasError('PwdNotMatched') && control.dirty;
          break;
      }
    }
  }
}
2

There are 2 best solutions below

2
Yong Shun On BEST ANSWER

You are set the matchPwdValidator validation to the "confirmPwd" FormControl. It can't find any control with name: "password" and "confirmPwd". You should get both controls from the root form group of the current form control.

const password = control.parent?.get('password');
const confirmPwd = control.parent?.get('confirmPwd');

Demo @ StackBlitz

0
codewithharshad On

I think you are doing password-matching validation all code here It's proper working and you change as per you need

html file

<div class="col-4">
      <form [formGroup]="registerForm" #form="ngForm">
        <div class="col-md-12">
          <div class="form-group required">
            <div class="services-form">
              <input
                maxlength="50"
                formControlName="Password"
                maxlength="50"
                placeholder="Password"
              />
              <label>Password</label>
            </div>
          </div>
        </div>

        <!-- Confirm password -->
        <div class="col-sm-6">
          <div class="form-group required">
            <div class="services-form">
              <input
                type="password"
                formControlName="ConfirmPassword"
                autocomplete="off"
                placeholder="Confirm password"
              />
              <label>Confirm password</label>
              <div
                *ngIf="
                  registerForm.get('ConfirmPassword')?.touched &&
                  registerForm.get('ConfirmPassword')?.invalid
                "
                class="input-error"
              >
                {{
                  !registerForm.get('ConfirmPassword')?.value
                    ? 'The confirm
              password field is required.'
                    : 'The password and confirm password
              fields do not match'
                }}
              </div>
            </div>
          </div>
        </div>
      </form>
    </div>

ts file :

registerForm!: FormGroup;

  constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
    this.registerForm = this.formBuilder.group(
      {
        Password: ['', [Validators.required]],
        ConfirmPassword: ['', Validators.required],
      },
      {
        validator: MustMatch('Password', 'ConfirmPassword'),
      }
    );
  }

must-match.validator.ts

import { FormGroup } from '@angular/forms';

// custom validator to check that two fields match
export function MustMatch(controlName: string, matchingControlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];
    const matchingControl = formGroup.controls[matchingControlName];

    if (matchingControl.errors && !matchingControl.errors?.['mustMatch']) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    if (control.value !== matchingControl.value) {
      matchingControl.setErrors({ mustMatch: true });
    } else {
      matchingControl.setErrors(null);
    }
  };
}

please check in stackBlitz

https://stackblitz.com/edit/angular13-reactive-form-validation-jqtqvw?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fmust-match.validator.ts,src%2Fapp%2Fapp.module.ts