User reactive form validator for password and password confirmation, getting undefined parameters in Angular 4

I am trying to implement a special validator to check if the password and password match. The problem is that the validator receives the undefined password and confirms the passage parameters. How to do it? The function works if I change the condition to === instead! ==, this correctly generates an error when the fields are the same. Does anyone know what the error is here?

signup.component.html

<div class="col-md-7 col-md-offset-1 col-sm-7"> <div class="block"> <div class="well"> <form (onSubmit)="onSubmit()" [formGroup]="signUpForm"> <div class="form-group"> <label for="username" class="control-label">Nombre de usuario:</label> <input type="text" class="form-control" formControlName="username" title="Please enter your username" placeholder="username"> <p class="help-block" *ngIf="signUpForm.get('username').hasError('required') && signUpForm.get('username').touched">El nombre de usuario es obligatorio</p> <p class="help-block" *ngIf="signUpForm.get('username').hasError('minlength') && signUpForm.get('username').touched">El nombre de usuario debe tener al menos 6 caracteres</p> <p class="help-block" *ngIf="signUpForm.get('username').hasError('maxlength') && signUpForm.get('username').touched">El nombre de usuario debe tener menos de 15 caracteres</p> </div> <div class="form-group"> <label for="email" class="control-label">E-mail:</label> <input class="form-control" formControlName="email" title="Please enter your email" placeholder=" example@gmail.com "> <p class="help-block" *ngIf="signUpForm.get('email').hasError('required') && signUpForm.get('email').touched">La dirección de email es obligatoria</p> <p class="help-block" *ngIf="signUpForm.get('email').hasError('email') && signUpForm.get('email').touched">Debe ingresar una dirección de correo válida</p> </div> <div class="form-group"> <label for="password" class="control-label">Contraseña:</label> <input type="password" class="form-control" formControlName="password" title="Please enter your password" [(ngModel)]="password"> <p class="help-block" *ngIf="signUpForm.get('password').hasError('required') && signUpForm.get('password').touched">Debe ingresar una contraseña</p> </div> <div class="form-group"> <label for="confirmedPassword" class="control-label">Confirmar Contraseña:</label> <input type="password" class="form-control" formControlName="confirmedPassword" title="Please re-enter your password" [(ngModel)]="confirmedPassword"> <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('required') && signUpForm.get('confirmedPassword').touched">La confirmación de contraseña no puede estar vacía</p> <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('passwordMismatch') && signUpForm.get('confirmedPassword').touched">Las contraseñas no coinciden</p> </div> <button type="submit" class="btn btn-success" [disabled]="!signUpForm.valid">Registrarse</button> <a routerLink="/signin" class="btn btn-default" style="">Ya tenes usuario? Logueate</a> {{ creationMessage }} </form> </div> </div> </div> 

signup.component.ts

 import { Component, OnInit, ViewChild, Input } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { CustomValidators } from '../../shared/custom-validators'; import { Observable } from 'rxjs/Observable'; @Component({ selector: 'app-signup', templateUrl: './signup.component.html', styleUrls: ['./signup.component.sass'] }) export class SignupComponent implements OnInit { signUpForm: FormGroup; user = { username: '', email: '', password: '' }; submitted = false; @Input() password=''; @Input() confirmedPassword=''; constructor() { } ngOnInit() { this.signUpForm = new FormGroup({ 'username': new FormControl(null, [Validators.required, Validators.minLength(6), Validators.maxLength(15)]), 'email': new FormControl(null, [Validators.required, Validators.email, Validators.minLength(5)]), 'password': new FormControl(null, [Validators.required]), 'confirmedPassword': new FormControl(null, [Validators.required, CustomValidators.passwordsMatch(this.password,this.confirmedPassword).bind(this)]) }); } onSubmit() { if (this.signUpForm.valid) { console.log(this.signUpForm.value); } } } 

Custom-validators.ts

  import { FormControl } from '@angular/forms'; export class CustomValidators{ public static passwordsMatch(password: string, confirmedPassword: string) { return (control: FormControl) : { [s: string]: boolean } =>{ //getting undefined values for both variables console.log(password,confirmedPassword); //if I change this condition to === it throws the error if the // two fields are the same, so this part works if (password !== confirmedPassword) { return { 'passwordMismatch': true } } else { //it always gets here no matter what return null; } } } } 
+22
javascript angular angular-reactive-forms angular-validation
source share
8 answers

import {AbstractControl, FormBuilder, FormGroup, Validators} of

enter your password into the group and do not need to use "ngModel".

 <div class="form-group row" formGroupName="passwords"> <div class="form-group"> <label for="password" class="control-label">Contraseña:</label> <input type="password" class="form-control" formControlName="password" title="Please enter your password"> <p class="help-block" *ngIf="signUpForm.get('password').hasError('required') && signUpForm.get('password').touched">Debe ingresar una contraseña</p> </div> <div class="form-group"> <label for="confirmedPassword" class="control-label">Confirmar Contraseña:</label> <input type="password" class="form-control" formControlName="confirmedPassword" title="Please re-enter your password"> <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('required') && signUpForm.get('confirmedPassword').touched">La confirmación de contraseña no puede estar vacía</p> <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('passwordMismatch') && signUpForm.get('confirmedPassword').touched">Las contraseñas no coinciden</p> </div> 

  buildForm(): void { this.userForm = this.formBuilder.group({ passwords: this.formBuilder.group({ password: ['', [Validators.required]], confirm_password: ['', [Validators.required]], }, {validator: this.passwordConfirming}), }); } 

add this custom function to verify the password and confirm the password

  passwordConfirming(c: AbstractControl): { invalid: boolean } { if (c.get('password').value !== c.get('confirm_password').value) { return {invalid: true}; } } 

Display error if password does not match

 <div style='color:#ff7355' *ngIf="userForm.get(['passwords','password']).value != userForm.get(['passwords','confirm_password']).value && userForm.get(['passwords','confirm_password']).value != null"> Password does not match</div> 
+35
source share

The problem is that you are mixing the reactive form module with the input approach . This forces you to get undefined when passing values ​​to the validator.

When using reactive forms you do not need to contact ng-model . Instead, you should access the field value from an instance of FormGroup .

I am doing something similar in a password validation application.

 public Credentials: FormGroup; ngOnInit() { this.Credentials = new FormGroup({}); this.Credentials.addControl('Password', new FormControl('', [Validators.required])); this.Credentials.addControl('Confirmation', new FormControl( '', [Validators.compose( [Validators.required, this.validateAreEqual.bind(this)] )] )); } private validateAreEqual(fieldControl: FormControl) { return fieldControl.value === this.Credentials.get("Password").value ? null : { NotEqual: true }; } 

Notice that the validator expects the FormControl field as a parameter and compares the field value with the Password field of the Credentials FormGroup field.

In HTML be sure to remove ng-model .

 <input type="password" class="form-control" formControlName="confirmedPassword" title="Please re-enter your password" > <!-- AND --> <input type="password" class="form-control" formControlName="password" title="Please enter your password"> 

Hope this helps!

+9
source share

Update FormGroup code as shown below in Angular5

  this.signUpForm = new FormGroup({ 'username': new FormControl(null, [Validators.required, Validators.minLength(6), Validators.maxLength(15)]), 'email': new FormControl(null, [Validators.required, Validators.email, Validators.minLength(5)]), 'password': new FormControl(null, [Validators.required]), 'confirmedPassword': new FormControl(null, [Validators.required]) }, this.pwdMatchValidator); 

Add the pwdMatchValidator function to your component

 pwdMatchValidator(frm: FormGroup) { return frm.get('password').value === frm.get('confirmedPassword').value ? null : {'mismatch': true}; } 

Add confirmation message to your template

 <span *ngIf="confirmedPassword.errors || signUpForm .errors?.mismatch"> Password doesn't match </span> 

Please find below angular working material.

Templete Code component password.component.html

  <form class="cahnge-pwd-form" (ngSubmit)="onSubmit()" name="passwordForm" [formGroup]="passwordForm" #formDir="ngForm"> <div fxLayout='column'> <mat-form-field> <input matInput name="password" placeholder="Password" [type]="hide ? 'text' : 'password'" formControlName="password" required> <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility_off' : 'visibility'}}</mat-icon> <mat-error *ngIf="password.invalid && (password.dirty || password.touched || isSubmit)"> <span *ngIf="password.errors.required"> Please enter a Password. </span> <span *ngIf="password.errors.maxlength"> Please enter a Email no more than 16 characters. </span> <span *ngIf="password.errors.minlength"> Please enter a password at least 6 characters. </span> </mat-error> </mat-form-field> <mat-form-field> <input matInput name="password" placeholder="Confirm Password" [type]="confirm_hide ? 'text' : 'password'" formControlName="confirm_password" required> <mat-icon matSuffix (click)="confirm_hide = !confirm_hide">{{confirm_hide ? 'visibility_off' : 'visibility'}}</mat-icon> <mat-error *ngIf="(confirm_password.invalid && (confirm_password.dirty || confirm_password.touched || isSubmit) || passwordForm.errors?.mismatch)"> <span *ngIf="confirm_password.errors || passwordForm.errors?.mismatch"> Password doesn't match </span> </mat-error> </mat-form-field> <div fxLayout='row' fxLayoutGap="10px"> <button type="submit" mat-raised-button color="primary">Submit</button> <button type="button" (click)="formDir.resetForm(passwordForm)" mat-raised-button color="warn">Cancel</button> </div> </div> </form> 

Component Code: password.component.ts

 import { Component, OnInit, AfterViewInit } from '@angular/core'; import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms'; import { ToastrService } from 'ngx-toastr'; import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { PasswordService } from './password.service'; import { PasswordValidation } from './confirm'; @Component({ selector: 'app-password', templateUrl: './password.component.html', styleUrls: ['./password.component.css'] }) export class PasswordComponent implements OnInit { passwordForm: FormGroup; isSubmit: boolean; constructor(private router: Router, private passwordService: PasswordService, private toastrService: ToastrService, private route: ActivatedRoute) { } ngOnInit() { this.passwordForm = new FormGroup({ 'password': new FormControl('', [ Validators.required, Validators.minLength(6), Validators.maxLength(16), ]), 'confirm_password': new FormControl('', [ Validators.required, Validators.minLength(6), Validators.maxLength(16), ]), }, this.pwdMatchValidator); } pwdMatchValidator(frm: FormGroup) { return frm.get('password').value === frm.get('confirm_password').value ? null : {'mismatch': true}; } get password() { return this.passwordForm.get('password'); } get confirm_password() { return this.passwordForm.get('confirm_password'); } onSubmit(formvalue):boolean { this.isSubmit = true; if (this.passwordForm.invalid) { return false; } else { this.passwordService.updatePassword(this.passwordForm.value) .subscribe((res) => { if (res.status == 'success') { this.toastrService.success(res.msg); this.router.navigate(['/change-password']); } }) return true; } } } 
+4
source share

There are two types of validators: the FormGroup validator and the FormControl validator . To check if two passwords match, you must add a FormGroup validator. Below is my example:

Note: this.fb is the entered FormBuilder

 this.newAccountForm = this.fb.group( { newPassword: ['', [Validators.required, Validators.minLength(6)]], repeatNewPassword: ['', [Validators.required, Validators.minLength(6)]], }, {validator: this.passwordMatchValidator} ); passwordMatchValidator(frm: FormGroup) { return frm.controls['newPassword'].value === frm.controls['repeatNewPassword'].value ? null : {'mismatch': true}; } 

and in the temple:

 <div class="invalid-feedback" *ngIf="newAccountForm.errors?.mismatch && (newAccountForm.controls['repeatNewPassword'].dirty || newAccountForm.controls['repeatNewPassword'].touched)"> Passwords don't match. </div> 

The key point here is the addition of the FormGroup validator as the second parameter to the group method.

+3
source share

When you create a validator, you pass the password and confirmedPassword values, but changes to these values ​​will not be displayed in the validator.

The two options that I see are as follows:

  • define your validator on FormGroup and find the values ​​from the two controls that you want to compare; or
  • since you are already attached to this , use this.password and this.confirmedPassword in your validator.
0
source share

I would do the same as Shailesh Ladumor, but adding the following line before returning to the validation function:

 c.get('confirm_password').setErrors({'noMatch': true}); 

So the check function looks like this:

 passwordConfirming(c: AbstractControl): { invalid: boolean } { if (c.get('password').value !== c.get('confirm_password').value) { c.get('confirm_password').setErrors({'noMatch': true}); return {invalid: true}; } } 

This will not only set the userForm hole as an invalid group of forms, but also set confirm_password as an invalid form control.

After that, you can call the following function in your template:

 public getPasswordConfirmationErrorMessage() { if (this.userForm.get('confirm_password').hasError('required')) { return 'You must retype your password'; } else if (this.userForm.get('confirm_password').hasError('noMatch')) { return 'Passwords do not match'; } else { return ''; } 

}

0
source share

If you need to perform validation in more than one field and you want to declare a validator during the creation of a form, you must use the FormGroup validator . The main problem with the form validator is that it gives the error a form, not a validating control, which leads to some inconsistencies in the template. Here is a reusable form validator that adds an error to both the form and the control

 // in validators.ts file export function controlsEqual( controlName: string, equalToName: string, errorKey: string = controlName // here you can customize validation error key ) { return (form: FormGroup) => { const control = form.get(controlName); if (control.value !== form.get(equalToName).value) { control.setErrors({ [errorKey]: true }); return { [errorKey]: true } } else { control.setErrors(null); return null } } } // then you can use it like ngOnInit() { this.vmForm = this.fb.group({ username: ['', [Validators.required, Validators.email]], password: ['', [ Validators.required, Validators.pattern('[\\w\\d]+'), Validators.minLength(8)]], confirm: [''], // no need for any validators here }, { // here we attach our form validator validators: controlsEqual('confirm', 'password') }); } 
0
source share
 const form = new FormGroup({ password: new FormControl('', Validators.minLength(2)), passwordConfirm: new FormControl('', Validators.minLength(2)), }, passwordMatchValidator); function passwordMatchValidator(g: FormGroup) { return g.get('password').value === g.get('passwordCenter code hereonfirm').value ? null : {'mismatch': true}; } 

You need to create a new service, and you can call up the created service, while you want to confirm the password and confirmation fields.

-one
source share

All Articles