Vue 兩個欄位聯合校驗典型例子--修改密碼

阿拉伯1999發表於2021-07-10

1、前言

  本文是前文《Vue Element-ui表單校驗規則,你掌握了哪些?》針對多欄位聯合校驗的典型應用。

  在修改密碼時,一般需要確認兩次密碼一致,涉及2個屬性欄位。類似的涉及2個屬性欄位的情況有:

  • 日期時間範圍,如果兩者都有值,則要求:結束時間>=開始時間。
  • 數量關係:數量下限<=數量上限。

  特點是兩個屬性值都是可變的。本文以校驗兩次密碼的一致性應用,給出兩個可變屬性值的欄位之間的聯合校驗的典型解決方案。

2、方案實現

2.1、實現程式碼

  先給出表單的程式碼:

<template>
  <div id="contentwrapper">
    <h5 class="heading" align=left>使用者管理 / 修改密碼</h5>
    <!-- 分隔線 -->
    <el-divider></el-divider>    
    <el-form ref="form" :model="form" :rules="rules" label-width="100px">
      <el-form-item label="原 密 碼:" prop="oldPasswd">
        <el-input v-model="form.oldPasswd" :type="password">
          <!-- input中加圖示必須要有slot="suffix"屬性,不然無法顯示圖示 -->
          <i slot="suffix" :class="icon" @click="showPassword"></i>
        </el-input>
      </el-form-item>   
      <el-form-item label="新 密 碼:" prop="newPasswd">
        <el-input v-model="form.newPasswd" :type="password">
          <i slot="suffix" :class="icon" @click="showPassword"></i>
        </el-input>
      </el-form-item>   
      <el-form-item label="確認密碼:" prop="confirmPasswd">
        <el-input v-model="form.confirmPasswd" :type="password">
          <i slot="suffix" :class="icon" @click="showPassword"></i>
        </el-input>
      </el-form-item>   
      <el-form-item>
        <el-button type="primary" style="width:160px" size="small" @click="submit()">確定</el-button>
        <el-button type="primary" style="width:160px" size="small" @click="cancel()">取消</el-button>
      </el-form-item>      
    </el-form>
  </div>
</template>
<script>
  import {passwordValidator} from '@/common/validator.js'
  export default {
    data() {
      // 比較兩次密碼是否相同
      const comparePasswdValidator = (rule, value, callback) =>{
        // 獲取獲取值的方法
        var getvaluesMethod = rule.getValuesMethod;
        // 呼叫getvaluesMethod方法,獲取物件值
        var formData = getvaluesMethod();
        
        // 有一個為空,可能還沒有輸入值,此時不比較
        if (formData.newPasswd == '' || formData.confirmPasswd == ''){
          return callback();
        }

        // ===========================================================
        // 比較兩次密碼

        // 兩個都有值,比較
        if (formData.newPasswd == formData.confirmPasswd){
          // 新密碼與確認密碼一致
          // 先清除兩個密碼的校驗告警提示,目的是清除另一個密碼的不一致的提示
          this.$refs['form'].clearValidate(['newPasswd','confirmPasswd']);
          callback();
        }else{
          callback(new Error('兩次密碼不一致'));
        }
      }

      return {
        form : {
          oldPasswd : '',
          newPasswd : '',
          confirmPasswd : ''
        },
        //用於改變Input型別
        password:"password",  
        //用於更換Input中的圖示
        icon:"el-input__icon el-icon-view", 
        // 校驗規則配置
        rules: {
          oldPasswd : [
            {required: true, message: "密碼不能為空", trigger: 'blur'}
          ],          
          newPasswd : [
            {required: true, message: "新密碼不能為空", trigger: 'blur'},
            {min: 6, max: 18, message: "新密碼6-18位", trigger: 'blur'},
            {validator: passwordValidator, trigger: 'blur'},
            {validator: comparePasswdValidator,trigger: 'blur','getValuesMethod':this.getValuesMethod},
          ],     
          confirmPasswd : [
            {required: true, message: "確認密碼不能為空", trigger: 'blur'},
            {min: 6, max: 18, message: "確認密碼6-18位", trigger: 'blur'},
            {validator: passwordValidator, trigger: 'blur'},
            {validator: comparePasswdValidator,trigger: 'blur','getValuesMethod':this.getValuesMethod},
          ],                   
        },        
      }
    },
    methods: {
      // 獲取值的方法,為所有需要多欄位聯合校驗的校驗器使用
      getValuesMethod(){
        return this.form;
      },

      // 密碼的隱藏和顯示
      showPassword(){
        //點選圖示是密碼隱藏或顯示
        if( this.password=="text"){
            this.password="password";
            //更換圖示
            this.icon="el-input__icon el-icon-view";
        }else {
            this.password="text";
            this.icon="el-input__icon el-icon-stopwatch";
        }
      },
      
      // 提交
      submit(){
        let _this = this;
        this.$refs['form'].validate(valid => {
          // 驗證通過為true,有一個不通過就是false
          if (valid) {
            _this.instance.changePasswd(_this.$baseUrl,_this.form).then(res => {
              if (res.data.code == _this.global.SucessRequstCode){
                // 提示修改成功
                alert("重新設定密碼已成功!");
                // 跳轉到首頁
                this.$router.push({
                  path: '/home',
                });                
              }else{
                if (!_this.commonFuncs.isInterceptorCode(res.data.code)){
                  alert(res.data.message);
                }
              }              
            }).catch(error => {
              console.log(error);
            }); 
          }          
        })
      },

      // 取消
      cancel(){
        // 跳轉到首頁
        this.$router.push({
          path: '/home',
        });            
      }
    }
  }
</script>  

  匯入的外部校驗器passwordValidator在/src/common/validator.js檔案中,程式碼如下:

/* 密碼校驗 */
export function passwordValidator(rule, value, callback) {
  const reg =/^[_a-zA-Z0-9@.#%&*!\-\$^]+$/;
  if(value == '' || value == undefined || value == null){
    callback();
  } else {
    if (!reg.test(value)){
      callback(new Error('密碼由英文字母、數字以及下列字元組成:@.#%&*!_-$^'));
    } else {
      callback();
    }
  }
}

2.2、程式碼說明

2.2.1、規則配置:

    // 校驗規則配置
    rules: {
      oldPasswd : [
        {required: true, message: "密碼不能為空", trigger: 'blur'}
      ],          
      newPasswd : [
        {required: true, message: "新密碼不能為空", trigger: 'blur'},
        {min: 6, max: 18, message: "新密碼6-18位", trigger: 'blur'},
        {validator: passwordValidator, trigger: 'blur'},
        {validator: comparePasswdValidator,trigger: 'blur','getValuesMethod':this.getValuesMethod},
      ],     
      confirmPasswd : [
        {required: true, message: "確認密碼不能為空", trigger: 'blur'},
        {min: 6, max: 18, message: "確認密碼6-18位", trigger: 'blur'},
        {validator: passwordValidator, trigger: 'blur'},
        {validator: comparePasswdValidator,trigger: 'blur','getValuesMethod':this.getValuesMethod},
      ],                   
    },        

  重點是newPasswd和confirmPasswd屬性,兩個配置了相同的規則集,校驗規則都為:

  • 值不能為空。
  • 長度為6-18位。
  • 符合密碼校驗器passwordValidator的規則,即密碼由英文字母、數字以及下列字元組成:@.#%&*!_-$^。
  • 比較密碼校驗器comparePasswdValidator,這個校驗器新增了一個自定義屬性getValuesMethod,屬性值為this的getValuesMethod方法,注意是方法,不是方法名。該條規則,要求methods中有一個getValuesMethod方法,並且實現comparePasswdValidator校驗器。

  這些規則一起作用,所有規則都通過校驗,屬性校驗才通過,並且檢測次序按照陣列的先後次序執行。

  校驗規則中,newPasswd和confirmPasswd屬性,都配置相同的comparePasswdValidator,是因為兩個欄位屬性值都是可變的。comparePasswdValidator排在規則的最後一條,即需要先滿足前面的校驗規則。

2.2.2、getValuesMethod方法

  // 獲取值的方法,為所有需要多欄位聯合校驗的校驗器使用
  getValuesMethod(){
    return this.form;
  },

  getValuesMethod方法,返回data中form資料物件。這是一個很犀利的操作,相當於提供了全域性的資料探針,可以在校驗器中訪問data的form資料物件,並且由於form的v-modal模型,確保資料的實時性,即無需擔心獲取不到其它屬性的最新取值。getValuesMethod方法,提供了資料繫結的另類思路。

2.2.3、comparePasswdValidator校驗器

  // 比較兩次密碼是否相同
  const comparePasswdValidator = (rule, value, callback) =>{
    // 獲取獲取值的方法
    var getvaluesMethod = rule.getValuesMethod;
    // 呼叫getvaluesMethod方法,獲取物件值
    var formData = getvaluesMethod();
    
    // 有一個為空,可能還沒有輸入值,此時不比較
    if (formData.newPasswd == '' || formData.confirmPasswd == ''){
      return callback();
    }

    // ===========================================================
    // 比較兩次密碼

    // 兩個都有值,比較
    if (formData.newPasswd == formData.confirmPasswd){
      // 新密碼與確認密碼一致
      // 先清除兩個密碼的校驗告警提示,目的是清除另一個密碼的不一致的提示
      this.$refs['form'].clearValidate(['newPasswd','confirmPasswd']);
      callback();
    }else{
      callback(new Error('兩次密碼不一致'));
    }
  }

  因為配置的自定義規則屬性'getValuesMethod'是一個指向this.getValuesMethod的方法,因此該屬性可以看作方法來呼叫:

    // 獲取獲取值的方法
    var getvaluesMethod = rule.getValuesMethod;
    // 呼叫getvaluesMethod方法,獲取物件值
    var formData = getvaluesMethod();

  方法屬性的呼叫結果,返回了指向this.form的資料物件,於是就可以隨意訪問該物件的屬性。

  在比較兩者之前,如果發現有一者為空,則返回。因為當前屬性輸入值之後,對端屬性可能還沒有輸入值,此時不應該比較。

    // 有一個為空,可能還沒有輸入值,此時不比較
    if (formData.newPasswd == '' || formData.confirmPasswd == ''){
      return callback();
    }

  兩次密碼比較:

    // ===========================================================
    // 比較兩次密碼

    // 兩個都有值,比較
    if (formData.newPasswd == formData.confirmPasswd){
      // 新密碼與確認密碼一致
      // 先清除兩個密碼的校驗告警提示,目的是清除另一個密碼的不一致的提示
      this.$refs['form'].clearValidate(['newPasswd','confirmPasswd']);
      callback();
    }else{
      callback(new Error('兩次密碼不一致'));
    }

  兩次密碼比較,如果兩個密碼一致,就輸出告警提示。如果一致,則需要先清除對端的告警提示,因為此時對端可能有”兩次密碼不一致“的提示。

      // 先清除兩個密碼的校驗告警提示,目的是清除另一個密碼的不一致的提示
      this.$refs['form'].clearValidate(['newPasswd','confirmPasswd']);

  clearValidate方法,是element-form的方法,作用是清除一個或多個校驗規則屬性的異常提示。

  這時,是否會發生”誤殺“情況呢?即清除操作將對端的其它異常提示也清除了。考慮到此時兩次密碼相同,且兩者校驗規則是相同的,由於此校驗規則排在最後,從檢測次序來說,是最後執行的,也就是說,執行到本校驗器時,其它校驗都通過了。因此,這種”誤殺“的情況不會發生。實際執行效果也是如此。

  另外,需要注意的,此時不能用下列程式碼代替上面clearValidate呼叫語句:

          // 執行對端的校驗
          if (rule.field == 'newPasswd')
          {
            // 如果當前屬性為newPasswd
            this.$refs['form'].validateField('confirmPasswd');
          }else{
            this.$refs['form'].validateField('newPasswd');
          }

  因為,此時正在執行校驗,再呼叫對端校驗,會導致對端呼叫comparePasswdValidator,而對端校驗結果發現兩次密碼一致,將再次呼叫對端(對端的對端,即本身)校驗,於是死迴圈了,導致呼叫堆疊溢位。因此,校驗器程式碼中,儘量不要再呼叫validateField方法。

2.3、校驗效果

  下面是一些效果圖:

  初始狀態:

Vue 兩個欄位聯合校驗典型例子--修改密碼

  修改確認密碼,離開輸入焦點:

Vue 兩個欄位聯合校驗典型例子--修改密碼

  進入新密碼輸入框,不輸入,離開輸入焦點:

Vue 兩個欄位聯合校驗典型例子--修改密碼

  修改確認密碼,刪除尾部的字元"8",離開輸入焦點,此時又回到了初始狀態。

相關文章