表單校驗功能在開發中是很常見的,尤其是後臺作業系統,當然面向使用者的前端頁面也會有表單功能,比如:登入、提交電話等場景。
早在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即可,即實時獲取輸入框內容,當然對應的對輸入框的校驗方法也會發生改變,否則就會出現每輸入一個數字就會出現“輸入內容錯誤”的提示。
以上就是自定義表單校驗功能開發和優化,通過這個表單功能總結也是告誡自己,開發功能時不能過於心急,應該仔細分析資料和邏輯,尋找最簡單直接的方法,避免走彎路。
歡迎小夥伴們指出不足指出~