typescript 如何让密码匹配自定义验证器在Angular 16中的密码字段上显示错误消息?

carvr3hs  于 2023-10-22  发布在  TypeScript
关注(0)|答案(2)|浏览(118)

我正在Angular 16中开发一个React式注册表单。
我做了一个自定义验证来检查表单字段passwordconfirm_password中的值是否匹配:

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

export const matchpassword : ValidatorFn = (control: AbstractControl):ValidationErrors|null => {

     let password = control.get('password');
     let confirm_password = control.get('confirm_password');
     if (password && confirm_password && password?.value != confirm_password?.value) {
        return { password_match : true }
     }
    return null; 
}

components\registration\registration.component.ts中,我有:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { matchpassword } from "../../custom-validators/matchpassword.validator";

@Component({
  selector: 'app-registration',
  templateUrl: './registration.component.html',
  styleUrls: ['./registration.component.scss']
})
export class RegistrationComponent implements OnInit {

  public registrationForm!: FormGroup;
  public isSuccess: Boolean = false;

  constructor(private formBuilder: FormBuilder) { }

  get form() { return this.registrationForm.controls }

  public registerUser() {

    if (this.registrationForm.status !== 'INVALID') {
      // Show success alert
      this.isSuccess = true;

      // Reset form
      this.registrationForm.reset();
    } else {
      return;
    }
  }

  ngOnInit() {
    this.registrationForm = this.formBuilder.group({
      firstName: ['', [Validators.required, Validators.minLength(3)]],
      lastName: ['', [Validators.required, Validators.minLength(3)]],
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(6), matchpassword]],
      confirm_password: ['', matchpassword],
      terms: ['', Validators.requiredTrue],
    }, {
      validators: matchpassword
    });
  }
}

components\registration\registration.component.html中,我有:

<div *ngIf="isSuccess" class="alert alert-success alert-dismissible fade show text-center">
    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
    Your signup was successful!
</div>

<form
    [formGroup]="registrationForm"
    (ngSubmit)="registerUser()"
    novalidate
  >
    <div class="form-element mb-2">
      <label for="firstName" class="form-label"> First name </label>
      <input
        type="text"
        id="firstName"
        formControlName="firstName"
        class="form-control form-control-sm"
      />
      <span
        class="invalid-feedback"
        *ngIf="form['firstName']?.touched && form['firstName'].errors?.['required']"
        >The <i>First name</i> field is required</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['firstName'].touched && form['firstName'].errors?.['minlength']"
        >The <i>First name</i> must be at least 3 characters long</span
      >
    </div>

    <div class="form-element mb-2">
      <label for="lastName" class="form-label"> Last name </label>
      <input
        type="text"
        id="lastName"
        formControlName="lastName"
        class="form-control form-control-sm"
      />
      <span
        class="invalid-feedback"
        *ngIf="form['lastName'].touched && form['lastName'].errors?.['required']"
        >The <i>Last name</i> field is required</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['lastName'].touched && form['lastName'].errors?.['minlength']"
        >The <i>Last name</i> must be at least 3 characters long</span
      >
    </div>

    <div class="form-element mb-2">
      <label for="email" class="form-label"> Email address </label>
      <input
        type="email"
        id="email"
        formControlName="email"
        class="form-control form-control-sm"
      />
      <span
        class="invalid-feedback"
        *ngIf="form['email'].touched && form['email'].errors?.['required']"
        >The <i>Email</i> field is required</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['email'].touched && form['email'].errors?.['email']"
        >Please provide a valid email address</span
      >
    </div>

    <div class="form-element mb-2">
      <label for="password" class="form-label"> Password </label>
      <input
        type="password"
        id="password"
        formControlName="password"
        class="form-control form-control-sm"
      />
      <span
        class="invalid-feedback"
        *ngIf="form['password'].touched && form['password'].errors?.['required']"
        >The <i>Password</i> field is required</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['password'].touched && form['password'].errors?.['minlength']"
        >The password must have al least 6 characters</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['password'].touched && form['password'].errors?.['passwordcomplexity']"
        >The password is not complex enough</span
      >
      <span
        class="invalid-feedback"
        *ngIf="form['password'].touched && registrationForm.errors?.['password_match']
        "
        >The passwords do not match</span
      >
    </div>

    <div class="form-element mb-2">
      <label for="confirm_password" class="form-label">
        Confirm password
      </label>
      <input
        type="password"
        id="confirm_password"
        formControlName="confirm_password"
        class="form-control form-control-sm"
      />
    </div>

    <div class="form-element mb-2">
      <input
        type="checkbox"
        formControlName="terms"
        id="terms"
        class="me-1"
      />
      <span class="text-terms text-muted"
        >I accept the
        <a href="#" class="text-success">Terms & conditions</a></span
      >
      <span
        class="invalid-feedback terms"
        *ngIf="form['terms'].dirty && form['terms'].errors?.['required']"
        >You must accept our Terms & conditions</span
      >
    </div>

    <div class="pt-2">
      <button
        type="submit"
        class="btn btn-sm btn-success w-100"
        [disabled]="!registrationForm.valid"
      >
        Submit
      </button>
    </div>
  </form>

Stackblitz

有一个stackblitz,所有代码都是**HERE**
目标
如果密码不匹配,我希望错误消息“密码不匹配”显示 * 在密码字段 *,如下所示:

问题
虽然密码被视为无效(提交按钮被禁用),但错误消息不会显示:

我做错了什么?

mbyulnm0

mbyulnm01#

matchpassword验证器需要添加到表单group中,而不是添加到某个单独的控件中。向控件添加验证程序并不给予对其他控件的访问权限。

this.registrationForm = this.formBuilder.group(
  {
    firstName: ['', [Validators.required, Validators.minLength(3)]],
    lastName: ['', [Validators.required, Validators.minLength(3)]],
    email: ['', [Validators.required, Validators.email]],
    password: [
      '',
      [Validators.required, Validators.minLength(6), passwordcomplexity],
    ],
    confirm_password: ['', Validators.required],
    terms: ['', Validators.requiredTrue],
  },
  {
    validators: [matchpassword],
  }
);

在解决这个问题之后,您需要调整错误消息,以便在表单组而不是表单控件出现错误时显示:

<span
  class="invalid-feedback"
  *ngIf="
    form['password'].touched &&
    registrationForm.errors?.['password_match']
  "
  >The passwords do not match</span
>

更新stackblitz:https://stackblitz.com/edit/stackblitz-starters-wbynzu?file=src%2Fapp%2Fcomponents%2Fregistration%2Fregistration.component.ts
注意:现在有一些重叠错误消息的问题,我没有修复,因为这与问题无关

g6ll5ycj

g6ll5ycj2#

如果有人会发现完整的代码,为我工作有用.
自定义验证器custom-validators\matchpassword.validator.ts

import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
 
 export const matchpassword: ValidatorFn = (
   control: AbstractControl
 ): ValidationErrors | null => {
   let password = control.get('password');
   let confirm_password = control.get('confirm_password');
   if (
     password &&
     confirm_password &&
     password?.value != confirm_password?.value
   ) {
     return { password_match: true };
   }
   return null;
 };

custom-validators\passwordcomplexity.validator.ts

import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms";
export const passwordcomplexity : ValidatorFn = (control: AbstractControl):ValidationErrors|null => {
    let password = control.value;

    let oneUppercase = /[A-Z]/.test(password);
    let oneLowercase = /[a-z]/.test(password);
    let oneDigit = /\d/.test(password);
    let oneSpecialCharacter = /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(password);
     
    if (!(oneUppercase && oneLowercase && oneDigit && oneSpecialCharacter)) {
      return { passwordcomplexity : true }
    }

    return null; 
}

app\components\registration\registration.component.ts中,我有:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { matchpassword } from "../../custom-validators/matchpassword.validator";
import { passwordcomplexity } from '../../custom-validators/passwordcomplexity.validator';

@Component({
  selector: 'app-registration',
  templateUrl: './registration.component.html',
  styleUrls: ['./registration.component.scss']
})
export class RegistrationComponent implements OnInit {

  public registrationForm!: FormGroup;
  public isSuccess: Boolean = false;

  constructor(private formBuilder: FormBuilder) { }

  get form() { return this.registrationForm.controls }

  public registerUser() {

    if (this.registrationForm.status !== 'INVALID') {
      // Show success alert
      this.isSuccess = true;

      // Reset form
      this.registrationForm.reset();
    } else {
      return;
    }
  }

  ngOnInit() {
    this.registrationForm = this.formBuilder.group(
      {
        firstName: ['', [Validators.required, Validators.minLength(3)]],
        lastName: ['', [Validators.required, Validators.minLength(3)]],
        email: ['', [Validators.required, Validators.email]],
        password: ['',[Validators.required, Validators.minLength(6), passwordcomplexity]],
        confirm_password: [''],
        terms: ['', Validators.requiredTrue],
      },
      {
        validators: [matchpassword],
      }
    );
  }
}

src\app\components\registration\registration.component.html中,我有:

<div *ngIf="isSuccess" class="alert alert-success alert-dismissible fade show text-center">
    <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
    Your signup was successful!
</div>

<form [formGroup]="registrationForm" (ngSubmit)="registerUser()" novalidate>
    <div class="form-element mb-2">
      <label for="firstName" class="form-label">
        First name
      </label>
      <input type="text" id="firstName" formControlName="firstName" class="form-control form-control-sm" />
      <span class="invalid-feedback"
        *ngIf="form['firstName']?.touched && form['firstName'].errors?.['required']">The <i>First name</i> field is required</span>
        <span class="invalid-feedback" *ngIf="form['firstName'].touched && form['firstName'].errors?.['minlength']">The <i>First name</i> must be at least 3 characters long</span>
    </div>

    <div class="form-element mb-2">
      <label for="lastName" class="form-label">
        Last name
      </label>
      <input type="text" id="lastName" formControlName="lastName" class="form-control form-control-sm" />
      <span class="invalid-feedback" *ngIf="form['lastName'].touched && form['lastName'].errors?.['required']">The <i>Last name</i> field is required</span>
      <span class="invalid-feedback" *ngIf="form['lastName'].touched && form['lastName'].errors?.['minlength']">The <i>Last name</i> must be at least 3 characters long</span>
    </div>

    <div class="form-element mb-2">
      <label for="email" class="form-label">
        Email address
      </label>
      <input type="email" id="email" formControlName="email" class="form-control form-control-sm" />
      <span class="invalid-feedback" *ngIf="form['email'].touched && form['email'].errors?.['required']">The
        <i>Email</i> field is
        required</span>
      <span class="invalid-feedback" *ngIf="form['email'].touched && form['email'].errors?.['email']">Please provide
        a valid email
        address</span>

    </div>

    <div class="form-element mb-2">
      <label for="password" class="form-label">
        Password
      </label>
      <input type="password" id="password" formControlName="password" class="form-control form-control-sm" />
      <span class="invalid-feedback" *ngIf="form['password'].touched && form['password'].errors?.['required']">The
        <i>Password</i> field is
        required</span>
      <span class="invalid-feedback" *ngIf="form['password'].touched && form['password'].errors?.['minlength']">The
        password must have al least 6 characters</span>
      <span class="invalid-feedback"
        *ngIf="form['password'].touched && form['password'].errors?.['passwordcomplexity']">The password is not
        complex enough</span>
      <span class="invalid-feedback" *ngIf="form['password'].touched && registrationForm.errors?.['password_match']
        ">The passwords do not match</span>
    </div>

    <div class="form-element mb-2">
      <label for="confirm_password" class="form-label">
        Confirm password
      </label>
      <input type="password" id="confirm_password" formControlName="confirm_password"
        class="form-control form-control-sm" />
    </div>

    <div class="form-element mb-2">
      <input type="checkbox" formControlName="terms" id="terms" class="me-1">
      <span class="text-terms text-muted">I accept the <a href="#" class="text-success">Terms &
          conditions</a></span>
      <span class="invalid-feedback terms" *ngIf="form['terms'].dirty && form['terms'].errors?.['required']">You
        must accept our Terms &
        conditions</span>
    </div>

    <div class="pt-2">
      <button type="submit" class="btn btn-sm btn-success w-100" [disabled]="!registrationForm.valid">
        Submit
      </button>
    </div>
</form>

为了 * 防止验证消息重叠 *,在SCSS中,我这样做是为了 * 在DOM中只显示第一条消息 *:

.invalid-feedback {
    display: none;

    &:first-of-type {
        display: inline-block;
    }
}

DEMO

stackblitz**HERE**演示。

相关问题