欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Angular5+ 自定义表单验证器

程序员文章站 2022-06-24 16:22:41
Angular自定义表单验证器 - 怎样实现“再次输入密码”的验证(两个controller值相等)(equalTo) ......

angular5+ 自定义表单验证器

custom validators

标签(空格分隔): angular


首先阐述一下遇到的问题:

  • 怎样实现“再次输入密码”的验证(两个controller值相等)(equalto)
  • 怎样反向监听(先输入“再次输入密码”,后输入设置密码)

解决思路:

  • 第一个问题,可以通过[abstractcontrol].root.get([targetname])来取得指定的controller,然后比较他们的值。
  • 第二个,可以通过[target].seterrors([errors])来实现。
  1. 这是一个我的自定义表单验证:
import {abstractcontrol, formgroup, validatorfn} from '@angular/forms';
import {g} from '../services/data-store.service';

export class myvalidators {
  private static isemptyinputvalue(value) {
    // we don't check for string here so it also works with arrays
    return value == null || value.length === 0;
  }
  private static isemptyobject(obj) {
    if (typeof obj === 'object' && typeof obj.length !== 'number') {
      return object.keys(obj).length === 0;
    }
    return null;
  }

  /**
   * 等于指定controller的值
   * @param targetname 目标的formcontrolname
   * @returns {(ctrl: formcontrol) => {equalto: {valid: boolean}}}
   */
  static equalto(targetname: string): validatorfn {
    return (control: abstractcontrol): {[key: string]: any} | null => {
      const target = control.root.get(targetname);
      if (target === null) {
        return null;
      }
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }
      return target.value === control.value ? null : {'equalto': { valid: false }};
    };
  }

  /**
   * 反向输入监听指定controller是否与当前值相等
   * @param targetname
   */
  static equalfor(targetname: string): validatorfn {
    return (control: abstractcontrol): {[key: string]: any} | null => {
      const target = control.root.get(targetname);
      if (target === null) {
        return null;
      }
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }
      if (target.value === control.value) {
        const errors = target.errors;
        delete errors['equalto'];

        if (this.isemptyobject(errors)) {
          target.seterrors(null);
        } else {
          target.seterrors(errors);
        }
        return null;
      }
      target.seterrors({ 'equalto': { valid: false } });
    };
  }

  ...
}

(注:)其中g.regex等的是全局变量。

  1. 然后formbuilder来实现:
import { component, oninit } from '@angular/core';
import {eventsservice} from '../../../services/events.service';
import {formbuilder, formgroup, validators} from '@angular/forms';
import {g} from '../../../services/data-store.service';
import {fade} from '../../../animations/fade.animation';
import {myvalidators} from '../../../directives/my-validators.directive';

@component({
  selector: 'app-sign-up',
  templateurl: './sign-up.component.html',
  styleurls: ['./sign-up.component.scss'],
  animations: [fade]
})
export class signupcomponent implements oninit {
  signform: formgroup; // 表单组formgroup
  submitting: boolean; // 是否可以提交
  validations = g.validations;

  constructor(private eventsservice: eventsservice, private formbuilder: formbuilder) {
    this.submitting = false;

    // 
    this.init();
  }

  ngoninit() {
    // 设置父组件标题
    this.eventsservice.publish('setsign', { title: '注册', subtitle: { name: '立即登录', uri: '/account/sign-in' } });
  }

  // 立即注册
  onsubmit() {
    console.log(this.signform.getrawvalue());
  }

  // 表单初始化
  private init() {
    this.signform = this.formbuilder.group({
      username: ['', validators.compose([validators.required, validators.maxlength(this.validations.usr_max)])],
      password: ['', validators.compose([
        validators.required,
        validators.minlength(this.validations.pass_min),
        validators.maxlength(this.validations.pass_max),
        myvalidators.equalfor('passwordconfirm')
      ])],
      passwordconfirm: ['', validators.compose([
        validators.required,
        validators.minlength(this.validations.pass_min),
        validators.maxlength(this.validations.pass_max),
        myvalidators.equalto('password')
      ])]
    });
  }
}

(注:)其中fade动画效果。

  1. 然后在html模板中,显示表单验证提示信息:
<form [formgroup]="signform" (ngsubmit)="onsubmit()" class="sign-form" @fade>

  <!-- 账号 -->
  <div class="input-group username">
    <span class="addon prev"><i class="civ civ-i-usr"></i></span>
    <input type="text"
      name="username"
      class="form-control form-control-left default"
      placeholder="请输入账号"
      formcontrolname="username"
      autocomplete="off">
    <ul class="errors" *ngif="signform.get('username').invalid && (signform.get('username').dirty || signform.get('username').touched)">
      <li *ngif="signform.get('username').haserror('required')" class="error">
        请输入您的账号!
      </li>
      <li *ngif="signform.get('username').haserror('maxlength')" class="error">
        账号不超过{{ validations.usr_max }}位!
      </li>
    </ul>
  </div> <!-- /.账号 -->
  
  <!-- 密码 -->
  <div class="input-group password">
    <span class="addon prev"><i class="civ civ-i-lock"></i></span>
    <input type="password"
      name="password"
      class="form-control form-control-left default"
      placeholder="请输入密码"
      formcontrolname="password">
    <ul class="errors" *ngif="signform.get('password').invalid && (signform.get('password').dirty || signform.get('password').touched)">
      <li *ngif="signform.get('password').haserror('required')" class="error">
        请输入您的密码!
      </li>
      <li *ngif="signform.get('password').haserror('minlength')" class="error">
        请输入至少{{ validations.pass_min }}位数的密码!
      </li>
      <li *ngif="signform.get('password').haserror('maxlength')" class="error">
        密码不超过{{ validations.pass_max }}位!
      </li>
    </ul>
  </div> <!-- /.密码 -->
  
  <!-- 重复密码 -->
  <div class="input-group password-confirm">
    <span class="addon prev"><i class="civ civ-i-lock"></i></span>
    <input type="password"
           name="passwordconfirm"
           class="form-control form-control-left default"
           placeholder="请再次输入密码"
           formcontrolname="passwordconfirm">
    <ul class="errors" *ngif="signform.get('passwordconfirm').invalid && (signform.get('passwordconfirm').dirty || signform.get('passwordconfirm').touched)">
      <li *ngif="signform.get('passwordconfirm').haserror('required')" class="error">
        请再次输入密码!
      </li>
      <li *ngif="signform.get('passwordconfirm').haserror('minlength')" class="error">
        请输入至少{{ validations.pass_min }}位数的密码!
      </li>
      <li *ngif="signform.get('passwordconfirm').haserror('maxlength')" class="error">
        密码不超过{{ validations.pass_max }}位!
      </li>
      <li *ngif="!signform.get('passwordconfirm').haserror('maxlength') && !signform.get('passwordconfirm').haserror('minlength') && signform.get('passwordconfirm').haserror('equalto')" class="error">
        两次密码输入不一致!
      </li>
    </ul>
  </div> <!-- /.重复密码 -->
  
  <!-- 提交按钮 -->
  <button type="submit"
          class="btn btn-primary btn-block submit"
          [disabled]="submitting || signform.invalid">立即注册</button>
  <!-- /.提交按钮 -->
  
</form>

最后,我们可以看到,实现了想要的效果:

Angular5+ 自定义表单验证器







(附:)完整的自定义表单验证器:

import {abstractcontrol, formgroup, validatorfn} from '@angular/forms';
import {g} from '../services/data-store.service';

export class myvalidators {
  private static isemptyinputvalue(value) {
    // we don't check for string here so it also works with arrays
    return value == null || value.length === 0;
  }
  private static isemptyobject(obj) {
    if (typeof obj === 'object' && typeof obj.length !== 'number') {
      return object.keys(obj).length === 0;
    }
    return null;
  }

  /**
   * 等于指定controller的值
   * @param targetname 目标的formcontrolname
   * @returns {(ctrl: formcontrol) => {equalto: {valid: boolean}}}
   */
  static equalto(targetname: string): validatorfn {
    return (control: abstractcontrol): {[key: string]: any} | null => {
      const target = control.root.get(targetname);
      if (target === null) {
        return null;
      }
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }
      return target.value === control.value ? null : {'equalto': { valid: false }};
    };
  }

  /**
   * 反向输入监听指定controller是否与当前值相等
   * @param targetname
   */
  static equalfor(targetname: string): validatorfn {
    return (control: abstractcontrol): {[key: string]: any} | null => {
      const target = control.root.get(targetname);
      if (target === null) {
        return null;
      }
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }
      if (target.value === control.value) {
        const errors = target.errors;
        delete errors['equalto'];

        if (this.isemptyobject(errors)) {
          target.seterrors(null);
        } else {
          target.seterrors(errors);
        }
        return null;
      }
      target.seterrors({ 'equalto': { valid: false } });
    };
  }

  /**
   * 验证手机号
   * @returns {(ctrl: formcontrol) => {mobile: {valid: boolean}}}
   */
  static get mobile() {
    return (control: abstractcontrol) => {
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }

      const valid = g.regex.mobile.test(control.value);

      return valid ? null : {
        'mobile': {
          valid: false
        }
      };
    };
  }

  /**
   * 验证身份证
   * @returns {(ctrl: formcontrol) => {idcard: {valid: boolean}}}
   */
  static get idcard() {
    return (control: abstractcontrol) => {
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }

      const valid = g.regex.id_card.test(control.value);

      return valid ? null : {
        'idcard': {
          valid: false
        }
      };
    };
  }

  /**
   * 验证汉字
   * @returns {(ctrl: formcontrol) => {cn: {valid: boolean}}}
   */
  static get cn() {
    return (control: abstractcontrol) => {
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }

      const valid = g.regex.cn.test(control.value);

      return valid ? null : {
        'cn': {
          valid: false
        }
      };
    };
  }

  /**
   * 指定个数数字
   * @param {number} length
   * @returns {(ctrl: formcontrol) => (null | {number: {valid: boolean}})}
   */
  static number(length: number = 6) {
    return (control: abstractcontrol) => {
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }

      const valid = new regexp(`^\\d{${length}}$`).test(control.value);

      return valid ? null : {
        'number': {
          valid: false
        }
      };
    };
  }

  /**
   * 强密码(必须包含数字字母)
   * @returns {(ctrl: formcontrol) => (null | {number: {valid: boolean}})}
   */
  static get strictpass() {
    return (control: abstractcontrol) => {
      if (this.isemptyinputvalue(control.value)) {
        return null;
      }

      const valid = g.regex.strict_pass.test(control.value);

      return valid ? null : {
        'strictpass': {
          valid: false
        }
      };
    };
  }
}