0

I am desperate because i cant get this to work. I have tried so many things but nothing works out of my knowledge.

Could someone help me please to get a "compare password" validation working?

Here is my registation.ts

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


@Component({
  selector: 'app-registration',
  templateUrl: './registration.component.html',
  styleUrls: ['./registration.component.css']
})

export class RegistrationComponent implements OnInit {

  constructor(
    private formBuilder: FormBuilder
  ) {
       this.createForm();
     }


  form: FormGroup;

  loading = false;
  helpblock = true;

  createForm() {
    this.form = this.formBuilder.group({
      email: new FormControl('', {
        validators: Validators.compose([
                      Validators.required,
                      Validators.minLength(5),
                      Validators.maxLength(40),
                      Validators.pattern(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)
                    ]),
        updateOn: 'change',
        }),
      username: new FormControl('', {
        validators: Validators.compose([
                      Validators.required,
                      Validators.minLength(3),
                      Validators.maxLength(15),
                      Validators.pattern(/^[a-zA-Z0-9]+$/)
                    ]),
        updateOn: 'change'
        }),
      password: new FormControl('', {
        validators: Validators.compose([
                      Validators.required,
                      Validators.minLength(7),
                      Validators.maxLength(35),
                      Validators.pattern(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{7,35}$/)
                    ]),
        updateOn: 'change'
        }),
        confirm_password: new FormControl('', {
          validators: Validators.compose([
                        Validators.required,
                        // this.matchingPasswords('password', 'confirm_password').bind(this)
                      ]),
          updateOn: 'change'
          }),
        compare_pws: Validators.call(this.matchingPasswords('password', 'confirm_password')),
    });
  }

  matchingPasswords(password, confirm_password) {
    console.log('0');
    return (group: FormGroup) => {
      if (group.controls[password].value === group.controls[confirm_password].value) {
        console.log('1');
        return null;
      } else {
        console.log('2');
        return { 'matchingPasswords': true };
      }
    };
  }

  register(form) {
    console.log(this.form);
  }


  ngOnInit() {}

}

Everything works fine. Only comparing passwords doesnt work for me..
Is there a way to solve this problem?

Andy
  • 25
  • 1
  • 5
  • I think you have to pass a custom validator(a plain function which takes a form control as parameter) in order for it to work... more on custom validators [here](https://blog.thoughtram.io/angular/2016/03/14/custom-validators-in-angular-2.html) – Osman Cea Jan 04 '18 at 19:14
  • Try this :) https://stackoverflow.com/a/43493648/6294072 – AT82 Jan 06 '18 at 11:35

4 Answers4

4

Write a Custom validator Class

import {AbstractControl} from '@angular/forms';
export class PasswordValidation {

    static MatchPassword(AC: AbstractControl) {
       let password = AC.get('password').value; // to get value in input tag
       let confirmPassword = AC.get('confirmPassword').value; // to get value in input tag
        if(password != confirmPassword) {
            console.log('false');
            AC.get('confirmPassword').setErrors( {MatchPassword: true} )
        } else {
            console.log('true');
            return null
        }
    }
}

Add it to Form Builder

validator: PasswordValidation.MatchPassword // your validation method

Credits for the answer - https://scotch.io/@ibrahimalsurkhi/match-password-validation-with-angular-2

More on reactive Forms and validators

Rahul Singh
  • 19,030
  • 11
  • 64
  • 86
  • Thank you for your answer! But i get this error: Cannot read property 'value' of null – Andy Jan 05 '18 at 00:40
  • @Andy can you replicate this on stackblitz as this works for me in the link i shared using the same scenario – Rahul Singh Jan 05 '18 at 06:12
  • I think it's worth pointing out that this validator goes at the form level validation and not at the control level validation. Doing something like `confirm_password: new FormControl('', PasswordValidation.MatchPassword)`will result in the null exception. – John Jan 26 '18 at 15:14
  • To solve the issue where if u type in confirmPassword field first and then password field and the error message doesn't disappear change custom validator code to following: `if (password != confirmPassword) { AC.get('cnfrmPassword').setErrors({ MatchPassword: true }) } else { AC.get('cnfrmPassword').setErrors({ MatchPassword: false }) }` – Sushmit Sagar Oct 30 '18 at 10:50
2

As Rahul Singh pointed out, the problem is solved with a custom validator. I found that the problem is that the injected element at the validator is the form control and (f. e. the password confirmation field) and you need to point out to the FormGroup instead so, this is a working example I have:

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


export class PasswordValidation {

    static MatchPassword(AC: FormControl) {
       return new Promise( resolve => {
         let password = AC.parent.controls['password'].value; // to get value in input tag
         let confirmPassword = AC.value; // to get value in input tag
         if(password === confirmPassword) {
           return resolve(null); // All ok, passwords match!!!
         } else {
            return resolve({"not_match": true})
         }
      });

    }
}

Then in your ts where you create your form group

let fg = formBuilder.group({
 password: ['', Validators.require],
 passwordConfirmation: ['', Validators.require, PasswordValidation.MatchPassword]
});
CastMart
  • 98
  • 5
  • better `passwordConfirmation: ['', [Validators.require, PasswordValidation.MatchPassword]]` because of https://forum.ionicframework.com/t/form-validation-error-expected-validator-to-return-promise-or-observable/90025/7 – netalex Dec 17 '18 at 09:37
0

I'm about a year late to the party here, but this post is one of the ones I found when I encountered this issue; but I ended up crafting a solution that seemed easier to me, and more likely that I'll be able to figure out what I did when I read the code again sometime in the future.

I have discovered while working on this problem, that a custom validator may not behave as expected when comparing two controls; I've found that every way I could think of to pass in the state of the control that validator was assigned to (password_confirm in my case), plus the current state of password, introduced the possibility that the wrong state of password would be used. This had the effect of working fine until you went back and changed the value of password in the form, which the custom validator either would not pick up, or would produce false positives/negatives. Your approach may not suffer from this, but be aware it's possible. You'll need three things to make this work.

First, you need a custom validator that will set the confirm field to invalid from the start(you wouldn't want to confuse or irritate your user by starting off saying they're good, and immediately saying they aren't); I tried manually setting it to invalid in NgOnInit and the constructor with this.registerForm.get('password_confirm').setErrors({isError: true});, but that doesn't work. This custom validator will check at form instantiation and will invalidate 'password_confirm` if the user clears the field.

notDefaultValidator(control: AbstractControl) {
    // in my case I set the default value to an empty string
    if(control.value === ''){
      return {isError: true};
    } else {
      return null;
    }
  }

Second, you need the fields themselves of course - this is a simplified example of my form:

this.registerForm = new FormGroup({
  'username': new FormControl('', Validators.required),
  'email': new FormControl('',
    [Validators.required, Validators.email]),
  'password': new FormControl('',
    [Validators.required, Validators.minLength(12)]),
  //the custom validator is used here
  'password_confirm': new FormControl(
    '', this.notDefaultValidator
  ),
  'first_name': new FormControl('', Validators.required),
  'last_name': new FormControl('', Validators.required),
});

Third, you simply subscribe to the changes in the form, manually setting the validity state of password confirm as you go like so:

//Not sure if Angular unsubscribes to this, so best to do it yourself
this.registerForm.valueChanges.subscribe((change) => {
  if (change.password !== change.password_confirm) {
    this.registerForm.get('password_confirm').setErrors({isError: true});
  } else if (change.password_confirm === '') {
    //this is needed in case the user empties both fields, else it would 
    //say they matched and therefore it's valid - the custom validator will 
    //not pick up on this edge case
    this.registerForm.get('password_confirm').setErrors({isError:true});
  } else if (change.password === change.password_confirm) {
    //this removes the previously set errors
    this.registerForm.get('password_confirm').setErrors(null);
  }
});

Again, I found that the custom validator field I had originally created for my password_confirm control introduced too many possibilities for edge case-type errors; and all though this is verbose, it works well, is easy to understand, and seems tolerant of multiple user input changes.

Hildy
  • 510
  • 5
  • 15
0

Try with angular custom validator for retype confirm values

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

export function RetypeConfirm(confirmpassword: string) {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
        if (control.value !== confirmpassword) {
           return { 'mismatch': true };
        }
        return null;
     };
}

Using in html file

<div *ngIf="yourFormController.errors?.mismatch">*Error message here</div>

working demo

dasunse
  • 2,839
  • 1
  • 14
  • 32