小程式自定義表單校驗

木風622發表於2020-01-03

表單校驗功能在開發中是很常見的,尤其是後臺作業系統,當然面向使用者的前端頁面也會有表單功能,比如:登入、提交電話等場景。

早在jquery一把梭的年代,表單校驗開發起來是灰常方便的,畢竟jquery為我們封裝好了各種api和方法,拿來用就好了;到了現階段,各大框架(element、小程式等)也封裝了相應的表單校驗方法,也都可以在基礎之上再進行進一步封裝已滿足不同的場景。但是,畢竟場景是無法預估的,有些場景通過原有api封裝可能開發起來更加有難度或者沒法實現(根本原因可能是因為本人技術太菜了),這種時候只能自己自定義校驗並提交了。

近期開發小程式時,就遇到了傳送簡訊功能的場景,對於前端也就是校驗手機號和是否勾選的問題。乍看起來並不複雜,但是由於剛接觸小程式不久,很多東西不是很熟悉,個人覺得原生提供的校驗方法不是很適用。

  • 首先從樣式方面:

checkbox勾選和標籤變色效果,前面我也吐槽過小程式核取方塊樣式問題(小程式初實踐總結),此處覆蓋css通過實驗並沒有達到理想效果。

  • 其次是邏輯校驗問題:

校驗和互動基本邏輯如下:

        ①、手機號校驗;

        ②、手機號校驗成功,自動勾選當前項;手機號校驗不成功,提示toast;

        ③、提交表單時,如果有勾選但手機號校驗不通過,則提示toast;如果至少有一項手機號校驗通過並且勾選當前項,其餘均未勾選,則提交符合要求資料;

基於上面兩個方面的考慮,決定表單才用自定義方式進行開發校驗。

一、基本表單結構

這部分沒什麼好說的,迴圈後臺返回的標籤,做出列表和提交按鈕即可,只列出主要部分程式碼。

<view wx:for="{{iconList}}" wx:key="{{index}}">
    <view class="checkbox" data-labelId="{{item.labelId}}" bindtap="{{choseFam}}"></view>
    <view>{{item.labelName}}</view>
    <input class="inputs" type="digit" maxlength="11" bindinput="inputBlur" data-labelId="{{item.labelId}}" placeholder="請輸入手機號" />
</view>
<view class="submit" bindtap="submitCheck">傳送簡訊邀請</view>複製程式碼

具體樣式略,效果圖如下:

小程式自定義表單校驗

二、基本互動和驗證

由於業務比較急,寫驗證的時候饒了很大一個圈,程式碼邏輯看上去也複雜了很多?,後面進行程式碼梳理的時候發現校驗邏輯可以簡化很多,優化後的校驗第三部分會提到,這部分貼出互動和校驗程式碼先~

//===手機號校驗
checkTel(num){
    let regMes = /^[1][3,5,6,7,8,9][0-9]{9}$/;
    if(!regMes.test(num)){
        return false
    }else{
        return true
    }
}

//===判斷陣列中物件是否有重複項
isRepeat(arr, str){
    let hash = {};
    for(let key in arr){
        if(hash[arr[key][str]]){
            return true
        }
        hash[arr[key][str]] = true
    }
    return false
}

//===自定義checkbox切換,同時給後設資料新增切換屬性,用於實時監聽切換效果
choseFam(e){
    let labelId = e.currentTarget.dataset.labelid;
    let choseList = this.data.iconList
    choseList.map((item)=>{
        if(item.labelId == labelid){
            item.checked = false
        }else{
            item.checked = true
        }
    })
    this.setData({
        iconList: choseList
    })
}

//===輸入框校驗及校驗通過自動勾選
inputBlur (e) {
    let labelid = e.currentTarget.dataset.labelid;
    let iptValue = e.detail.value;
    let fromObj = this.data.famTelObj;
    let choseFamlist = this.data.iconList;
    // 校驗通過時,收集手機號,同時對應紅點選中
    if(iptValue.length == 11){
        if (this.checkTel(iptValue)) {
            fromObj[labelid] = iptValue
            choseFamlist.map((item)=>{
                if(item.labelId == labelid){
                    item['mobile'] = iptValue
                    if (!item.checked) {
                        item.checked = true
                    }
                }
            })
        } else {
             // 校驗不通過時,清空對應的值,防止刪除了輸入值,依然存在的情況
            fromObj[labelid] = '‘
            delete fromObj[labelid]
            choseFamlist.map((item)=>{
                if(item.labelId == labelid){
                    item['mobile'] = ''
                }
            })
            showToast('請輸入正確的手機號')
        }
        this.setData({
            famTelObj: fromObj,
            iconList: choseFamlist
        })
    }else{
        fromObj[labelid] = ''
        delete fromObj[labelid]
        choseFamlist.map((item)=>{
            if(item.labelId == labelid){
                item['mobile'] = ''
            }
        })
    }
}複製程式碼

簡單說下輸入框邏輯,在此處我考慮通過一個物件手機輸入框的資訊,校驗通過時更新資料同時更新列表內容,校驗不通過則清空此物件的當前項,並吧列表裡當前項清空。(當然後面優化發現,用物件收集資訊這個操作完全是多餘的,直接控制列表各項就好了)。

最後是表單校驗邏輯,此處是優化前邏輯(校驗較複雜,可以直接忽略直接看優化後的邏輯???),不做過多解釋,直接上程式碼:

submitCheck () {
    let choseFamlist = this.data.iconList;
    let famObj = this.data.famTelObj;
    let choseFamTrue = choseFamlist.filter(item => item.checked);
    let famObjArr = Object.values(famObj);
    let famObjArrVal = [];
    if (famObjArr.length) {
        famObjArrVal = famObjArr.filter(item => item)
    }        
    // 提交時校驗
    if (choseFamTrue.length == 0 && famObjArrVal.length == 0) {
        showToast('請選擇邀請的家人') 
    } else if (choseFamTrue.length !== 0 && famObjArrVal.length == 0) {
        showToast('請輸入正確的手機號')
    } else if (choseFamTrue.length == 0 && famObjArrVal.length !== 0) {
        showToast('請勾選邀請的家人')
    } else {
        if(choseFamTrue.length > famObjArrVal.length){
            console.log('存在勾選但是未輸入手機號情況')
            showToast('請輸入正確的手機號') 
        }else if(choseFamTrue.length < famObjArrVal.length){
            console.log('輸入比選中的多')
            let subArr = choseFamlist.filter((item)=>{
                return item.checked && item.mobile
            })
            let checkInputNo = choseFamlist.filter((item)=>{
                return item.checked && !item.mobile
            })
            if(subArr.length){
                if(checkInputNo.length){
                    console.log('存在同一個選中和輸入的,但是也存在選中未輸入的')
                    showToast('請輸入正確的手機號')
                }else{
                    if(this.isRepeat(subArr, 'mobile')){
                        console.log('提交項有重複手機號')
                        showToast('請勿輸入重複手機號')
                    }else{
                        console.log('只提交勾選和輸入都有的且不重複手機號')        
                        this.setData({                                
                            submitTelArr: subArr                            
                        })
                        //提交資料,請求介面                            
                        this.submitData()                        
                    }                    
                }                
            }else{                    
                console.log('選中和輸入的沒有相同的')                    
                showToast('請輸入正確的手機號')                
            }                
        }else{                
            console.log('勾選和填寫一樣多')                
            let checkInputNo = choseFamlist.filter((item)=>{                    
                return item.checked && !item.mobile                
            })                
            if(checkInputNo.length){                    
                console.log('存在勾選但是未填寫手機號')                   
                showToast('請輸入正確的手機號')                
            }else{                    
                //獲取彈窗應該展示的手機號等資訊內容                    
                let subArr = choseFamlist.filter((item)=>{                        
                    return item.checked && item.mobile                    
                })                    
                if(this.isRepeat(subArr, 'mobile')){                           
                    console.log('提交項有重複手機號')                        
                    showToast('請勿輸入重複手機號')                    
                }else{                        
                    console.log('success')                        
                    this.setData({                            
                    submitTelArr: subArr                        
                })                        
                //提交資料,請求介面                        
                this.submitData()                    
            }                
        }            
    }        
}複製程式碼

話不多說,只看程式碼就覺得這個邏輯肯定是走了彎路了,讓我們直接看優化後的校驗邏輯。

三、優化後的校驗

基礎的手機號、輸入框校驗、提交表單邏輯都不變,只優化校驗部分。由於在處理核取方塊和輸入框時,分別把選中欄位和手機號欄位新增到了資料列表裡,因此我們只要通過資料列表去判斷各項即可,不必再單獨收集資料去做多餘的校驗了。

//只寫了大的邏輯判斷,未做具體資料的處理
submitCheck(){
    let choseFamlist = this.data.iconList;
    //收集選中同時校驗通過的各項
    let checkAndVal = choseFamlist.filter((item) => {            
        return item.checked && item.mobile        
    });
    //收集只選中,但是手機號校驗不通過各項        
    let checkNoVal = choseFamlist.filter((item) => {            
        return item.checked && !item.mobile        
    });
    //收集未選中,但是手機號校驗通過各項        
    let noCheckAndVal = choseFamlist.filter((item) => {            
        return !item.checked && item.mobile        
    });        
    if(checkAndVal.length){            
        if(checkNoVal.length){                
            console.log('有正確的,但是存在勾選未填的')            
        }else{                
            if(this.isRepeat(checkAndVal, 'mobile')){                    
                console.log('可提交,但有重複的')                
            }else{                    
                console.log('提交')                
            }            
        }        
    }else{            
        if(checkNoVal.length){                
            console.log('沒有正確的,存在勾選未填的')            
        }else{                
            if(noCheckAndVal.length){                    
                console.log('沒有正確的,存在只輸入值的')                
            }else{                    
                console.log('啥都沒有填')                
            }            
        }        
    }
}複製程式碼

這樣看上去,邏輯就很清晰了,只通過對三種情況的陣列進行處理比較即可。

四、輸入框輸入監聽事件

在最初獲取輸入框輸入內容時,是通過bindblur事件獲取的,即輸入框失去焦點時獲取輸入內容進行校驗,在開發者工具裡是沒有問題的,但是在真機上回有問題。

初次輸入內容,點選提交是可以收集到輸入值的;但是當對輸入框內容進行修改後,再次直接點選提交,會發現提交的依舊是修改前的內容,如果提交第二次則會提交最新的資料。也就是說,在真機上不能很準確的獲取bindblur這種輸入框失焦事件,進而導致資料無法及時更新。

解決辦法也很簡單,把bindblur事件替換成bindinput即可,即實時獲取輸入框內容,當然對應的對輸入框的校驗方法也會發生改變,否則就會出現每輸入一個數字就會出現“輸入內容錯誤”的提示。

以上就是自定義表單校驗功能開發和優化,通過這個表單功能總結也是告誡自己,開發功能時不能過於心急,應該仔細分析資料和邏輯,尋找最簡單直接的方法,避免走彎路。

歡迎小夥伴們指出不足指出~


相關文章