简体   繁体   中英

Angular custom validation: ensure form control is invalid when the form group has validation errors

I am working on reactive forms in angular. I have set up fields for Password and Confirm password as shown below. I am using custom validation to make sure that the Password field matches the Confirm password field. The problem is, although, on console logging, i can see that the forms error property has mismatch set to true , the confirm password field is still valid and thus,the form is still valid.

  1. How can I ensure that the Confirm Password field is also invalid when the form has mismatch property set in errors property?
  2. If I attach passwordMatchValidator to confirmPassword (in formcontrol definition) instead of setting up in formGroup level, then how do i set up my validation?

My component template

    <form [formGroup]="registerForm" (ngSubmit)="onRegister()">
        <div class="form-group">
        <input type="password"
        class="form-control" placeholder="Password" formControlName="password">
        <div class="invalid-feedback"  *ngIf="registerForm.get('password').hasError('required')">Password is required</div>
        <div class="invalid-feedback" *ngIf="registerForm.get('password').hasError('minlength')">Password must be at least 5 characters</div>
        <div class="invalid-feedback" *ngIf="registerForm.get('password').hasError('maxlength')">Password cannot exceed 8 characters</div>
      </div>
      <div class="form-group">
        <input type="password"
        class="form-control" placeholder="Confirm Password" formControlName="confirmPassword">
        <div class="invalid-feedback" *ngIf="registerForm.get('confirmPassword').hasError('required')">Password is required</div>
        <div class="invalid-feedback" *ngIf="registerForm.hasError('mismatch')">Password must match</div>
      </div>
     </form>

Css to show/hide error feedback

.ng-valid{
    border: 1px solid green;
}
.ng-invalid.ng-touched + .invalid-feedback{
    display: block;
}

And my component looks like following:

export class RegisterComponent implements OnInit {

 registerForm: FormGroup

  constructor(private fb: FormBuilder) { }
  
  ngOnInit(): void {
    this.registerForm = this.fb.group({
      "password": [null, [Validators.required, Validators.minLength(5), Validators.maxLength(8)]],
      "confirmPassword": [null, [Validators.required]]
    },{
      validators:this.passwordMatchValidator
    });
  }
  passwordMatchValidator(f:FormGroup):{[s:string]:boolean} {
    return f.controls['password'].value === f.controls['confirmPassword'].value ? null : { 'mismatch': true }
  }

  onRegister() {
    console.log(this.registerForm)
  }
}

I have setup a debugger inside passwordMatchValidator and tried console logging as shown below在此处输入图像描述

I have used below code for password validation.

 //In init method this.signUpForm = this._fb.group({ password: ['', [Validators.required, Validators.minLength(8), Validators.pattern(this._config.PASSWORD_REGEX) ] ], confirmPassword: ['', Validators.required ] }); this.signUpForm.get('password').valueChanges.subscribe(value => this.validatePassword(value)); //method validatePassword(value: any): void { let ctrl = this.signUpForm.get('password'); this.numberError = true; this.letterError = true; this.lengthError = true; if (/[az]/.test(value) && /[AZ]/.test(value)) { this.letterError = false; } if (/[0-9]/.test(value)) { this.numberError = false; } if (value.length >= 8) { this.lengthError = false; } } //Method match text matchText(controlType: string): void { let control1 = null, control2 = null; if (controlType == 'password') { control1 = this.signUpForm.get('password'); control2 = this.signUpForm.get('confirmPassword'); } if (control1.value && control2.value) { if (control1.value.= control2.value) { control2.setValidators(this;matchValidator()). } else { control2.setValidators([Validators;required]). } control2;updateValueAndValidity(): } } //match validator method matchValidator(): ValidatorFn { return (c: AbstractControl): { [key: string]: boolean } | null => { return { 'match'; false }; }; }
 <mat-form-field> <input autocomplete="nope" formControlName="password" matInput placeholder="PASSWORD" [type]="hidePassword? 'password': 'text'" (change)="matchText('password')" maxlength="30"> <mat-icon matSuffix (click)="hidePassword =?hidePassword">{{hidePassword: 'visibility'. 'visibility_off'}}</mat-icon> <mat-hint align="start" class="signup_hint"> <span>PASSWORD_MUST_HAVE..:</span> <br> <span [ngClass]="{'hint-error'. (((signUpForm.get('password').dirty || signUpForm.get('password').touched) && signUpForm.get('password').invalid) && signUpForm?get('password').?errors..required) || signUpForm?get('password').?errors.,minlength || lengthError: ''. .(((signUpForm.get('password').dirty || signUpForm.get('password').touched) && signUpForm.get('password')?invalid) && signUpForm.get('password')?.errors.?required) &&.signUpForm?get('password').;errors;:minlength &&.lengthError }">&nbsp? &nbsp.HINT_8_CHARACTERS</span> <br> <span [ngClass]="{'hint-error'? signUpForm.get('password'),:errors.?pattern && letterError. ''? .signUpForm;get('password');:errors.?pattern &&.letterError }">&nbsp? &nbsp. HINT_UPPER_AND_LOWER</span> <br> <span [ngClass]="{'hint-error', signUpForm:get('password').?errors.?pattern && numberError. ''; ;signUpForm?get('password'):.errors?:pattern &&.numberError }">&nbsp. &nbsp. HINT_AT_LEAST_ONE</span> <br/> </mat-hint> </mat-form-field> <mat-form-field> <input autocomplete="nope" formControlName="confirmPassword" matInput placeholder="CONFIRM_PASSWORD" [type]="hidePassword. 'password'. 'text'" (change)="matchText('password')" (paste)="$event.preventDefault()"> <mat-icon matSuffix (click)="hidePassword =.hidePassword">{{hidePassword? 'visibility'. 'visibility_off'}}</mat-icon> <mat-error *ngIf="(signUpForm?get('confirmPassword').dirty || signUpForm?get('confirmPassword'):touched) && signUpForm.get('confirmPassword').invalid">(signUpForm.get('confirmPassword')?.errors?.required ? 'ERR_REQUIRED_FIELD' : 'ERR_PASSWORD_MISMATCH')</mat-error> </mat-form-field>

you can use a css like:

/*Only border green/red the "form controls" nor the div*/

.form-control.ng-valid{
    border: 1px solid green;
}
.form-control.ng-invalid.ng-touched{
    border: 1px solid red;
}

.ng-invalid.ng-touched + .invalid-feedback{
    display: block;
}
/*Is invalid? the formControls valid and touched inside border red*/
.ng-invalid .form-control.ng-valid.ng-touched 
{
    border: 1px solid red!important;

}

See that the ng-invalid is the formgroup itself (use Navigators tools F12 to see where the class is applied)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM