vux之x-input使用以及原始碼解讀

三隻萌新發表於2018-11-03

前言

  1. 近期專案中使用的vux中的input,以及使用自定義校驗規則和動態匹配錯誤提示,有時間記錄下自己的使用經歷和原始碼分析。希望大家多多指正,留言區發表自己寶貴的建議。

詳解

  1. 列舉官方文件中常用的幾個屬性的使用方法,程式碼如下
 <group ref="group">
      <x-input v-model="name"
        class="vux-input__name"
        title="名字"
        placeholder="tell me your name"
        required
        :is-type="checkNameValid"
        @on-change="onValueChange">
        <div slot="label"
          class="name__icon">
          <icon type="success"></icon>
        </div>
      </x-input>
    </group>
    
    
複製程式碼

官方文件有詳細的解釋,required屬性表示此選項為必填,is-type可以繫結一個函式,作為校驗,這個函式得返回一個物件。格式如下

 checkValid(name) {
      return {
        valid: name === '三隻萌新',
        msg: '你不是萌新'
      }
    }
複製程式碼

valid可以設定為你的校驗規則,需要返回一個布林值,msg是錯誤的提示資訊。
vux本身寫好幾種校驗方式,如果使用email,china-name,china-mobile這幾種方式直接繫結字串即可。
solt插槽如slot="label"用於自定義title,原始碼如下

 <slot name="label">
    <label class="weui-label"
      :class="labelClass"
      :style="{width: labelWidth || $parent.labelWidth || labelWidthComputed, textAlign: $parent.labelAlign, marginRight: $parent.labelMarginRight}"
      v-if="title"
      v-html="title"
      :for="`vux-x-input-${uuid}`"></label>
    <inline-desc v-if="inlineDesc">{{ inlineDesc }}</inline-desc>
 </slot>

複製程式碼

分析:class="labelClass"動態繫結樣式以物件的形式返回一個{[className]:Boolean}的格式的物件

 labelClass() {
      return {
        'vux-cell-justify':
          this.$parent.labelAlign === 'justify' ||
          this.$parent.$parent.labelAlign === 'justify'
      }
    }
複製程式碼

label-align
同樣的方式檢視他父級是否有labelAlign屬性,vux-cell-justify類名對應的樣式沒有應用。

使用場景

場景1

假設在一個提交頁面,當我們提交時判斷輸入框中的值是否是符合我們的要求,如果不符合,給出錯誤提示,如果符合提交後將輸入框中的資料清空。

需求:

  • 如果還有停留在本頁面我們需要將上一次的資料全部清空

問題:

  • 我們需要初始化值,但是會發現如果我們設定了required後校驗還是會觸發。如何讓資料清空並且讓校驗也清空。

解決方法:

  • 文件中寫了reset可以重置輸入框值,清除錯誤資訊 使用方式:
  • 在x-input外層的group標籤上繫結ref來訪問子元件。因此我們可以通過 this.$refs.group.$children獲取到input元件集合並且可以使用元件中定義的reset方法 檢視原始碼
  • 如果你的專案中已經安裝了vux可以通過安裝Search node_modules查詢node_modules資料夾中vux安裝包路徑為vux/src/components/x-input/index.vue檔案 reset方法原始碼如下:
reset(value = '') {
      this.dirty = false
      this.currentValue = value
      this.firstError = ''
      this.valid = true
    }
複製程式碼

回到我們的業務邏輯中當我們點選提交按鈕時程式碼如下

 onSubmitClick() {
      if (!this.isInvalid) {
        this.$refs.group.$children.forEach(child => {
          child.reset()
        })
      } else {
      // 展示提示資訊
        this.isShowToast = true
      }
複製程式碼

本以為這樣就可以清空資料了,沒想到點選按鈕時資料是清空了,但是還是有報錯圖示顯示。

提交後的截圖
通過vue-devtools可以看到
除錯結果

valid的值為false檢視vux原始碼檢視涉及到valid程式碼如下

validate() {
 // ...省略與本次無關的校驗方法
if (!this.currentValue && this.required) {
        this.valid = false
        this.errors.required = '必填哦'
        this.getError()
        return
        if (typeof this.isType === 'function') {
        /* 
          取出自定義函式中的校驗結果 是一個Boolean
          checkNameValid(name) {
            return {
              valid: name === '三隻萌新',
              msg: '你不是萌新'
            }
          }
        */
        const validStatus = this.isType(this.currentValue)
        this.valid = validStatus.valid
        if (!this.valid) {
        // 如果校驗值無效將自定義校驗的msg賦值給errors物件下的format
          this.errors.format = validStatus.msg
          this.forceShowError = true
          this.getError()
          return
        } else {
        // 如果校驗值有效則將error物件下的format刪除    
          delete this.errors.format
        }
        // 如果都符合將valid賦值為有效
      this.valid = true
    }
}
複製程式碼

validate函式校驗當前是否有值,是否為必填,如果當前值的校驗方式是函式,將校驗結果賦值給valid。如果valid是false則將自定義的msg統一儲存在errors物件下,errors是用來儲存不同型別的錯誤資訊。 然後執行getError函式

  getError() {
      let key = Object.keys(this.errors)[0]
      this.firstError = this.errors[key]
      console.log('firstError' + this.firstError)
    }
複製程式碼

Object.keys(this.errors)返回errors物件下的所有可列舉屬性,並且取第一個作為鍵名,取出對於的值賦值給firstError ,firstError是提示框文字

  <toast v-model="showErrorToast"
      type="text"
      width="auto"
      :time="600">{{ firstError }}</toast>
複製程式碼

當點選錯誤圖示判斷是否有firstError,shouldToastError未傳入值預設為true,點選時如果valide校驗為錯誤時會觸發getError函式將錯誤提示賦值給firstError,所以會將fistError對應的提示資訊顯示出來。而圖示的顯示與否與valid有關,其中一個條件是valid為false時才會顯示。

 <icon @click.native="onClickErrorIcon"
        class="vux-input-icon"
        type="warn"
        :title="!valid ? firstError : ''"
        v-show="showWarn"></icon>
        
 shouldToastError: {
      type: Boolean,
      default: true
    }
  showWarn() {
      return (
        !this.novalidate &&
        !this.equalWith &&
        !this.valid &&
        this.firstError &&
        (this.touched || this.forceShowError)
      )
    }
  onClickErrorIcon() {
      if (this.shouldToastError && this.firstError) {
        this.showErrorToast = true
      }
      this.$emit('on-click-error-icon', this.firstError)
    }
複製程式碼

分析了上面的程式碼,為什麼執行了reset方法後,校驗報錯還是在,原因是valid依然還是false,導致showWarn返回值是ture,而reset中方法中明明將valid設定為true了,為什麼最後結果為false。

watch:{
      currentValue(newVal, oldVal) {
           if (newVal && this.equalWith) {
            if (newVal.length === this.equalWith.length) {
              this.hasLengthEqual = true
            }
            this.validateEqual()
          } else {
            this.validate()
          }
      }
}
複製程式碼

因為監聽了input繫結currentValue的值,當reset方法執行的時候this.currentValue = ' ' 觸發了變動執行validate方法,導致再次給this.valid賦值false。
該如何解決這個問題,問題發生的原因是currentValue發生變化導致觸發validate方法校驗,所以我們只要當執行reset方法後不觸發currentValue改變就可以不觸發validate方法校驗
方法一:

onSubmitClick() {
    this.$refs.group.$children.forEach(child => {
     // 這次reset是將currentValue全部置為""
      child.reset()
    })
    this.$nextTick(() => {
    // 當所以input的值都置為空後在此執行reset方法,這次前後currentValue沒有發生變化不會觸發validate校驗所以valide為true不會導致圖示出現
      this.$refs.group.$children.forEach(child => {
        child.reset()
      })
    })
}
複製程式碼

方法二: 其實想做的就是在reset方法執行之前將currentValue置為空

created(){
    this.currentValue =
      this.value === undefined || this.value === null
        ? ''
        : this.mask ? this.maskValue(this.value) : this.value
},
props:{
    value: [String, Number]
},
watch:{
    value(val) {
      this.currentValue = val
    }
}
複製程式碼

可以通過傳入value來改變currentValue的值,將v-model="name"繫結值的方式改為:value="name"

onSubmitClick() {
    this.name = ''
    this.$nextTick(() => {
      this.$refs.group.$children.forEach(child => {
        child.reset()
      })
    })
}
複製程式碼

場景2

當我們點選提交時,如果有校驗選項不符合規則能提示相匹配的警告

data(){
    return {
        message: '還未填寫資訊'
    }
}
複製程式碼

將message提示資訊初始值設定為還未填寫資訊,當我們未進行填寫資訊的時候點選提交顯示。然後使用on-change函式繫結校驗規則,實時更新message對應的提示語,業務邏輯如下:

     onValueChange() {
      // 多次使用賦值給變數
      const children = this.$refs.group.$children
      let statusList = []
      // 篩選出有值的,作為是否全部未填的判斷依據 如果length小於1則還沒填寫任何內容
      statusList = children.filter(item => {
        return item.currentValue
      })
      if (statusList.length < 1) {
        this.message = '還未填寫資訊'
        return
      }
      // 找到第一個沒有值的那一項,如果都有則返回undefined
      const firstInvalid = children.find(item => {
        return !item.valid
      })
      if (firstInvalid !== undefined) {
        this.message = `請填寫正確的${firstInvalid.title}`
      }
      // 顯示的將是否有效賦值給valid增加程式碼可讀性
      this.valid = Boolean(firstInvalid)
    }
複製程式碼

github

程式碼地址

相關文章