微信小程序实现表单校验功能


Posted in Javascript onMarch 30, 2020

小程序SDK版本 1.4

表单校验之难

如果要问微信小程序最难实现的公共业务是什么?应该是表单校验,没有之一。原因如下:

表单组件在数量上达到11个,居各类组件之首。当然幸运的是,并不是所有的都需要校验。
而这些组件操作方式多样,可分为滑动、(多行)输入、点击、点击+滑动。
即使是同一个组件,因为业务场景不同就会有不同的校验规则。
更麻烦的是,这些组件之间经常还会联动或者关联校验。

但是,作为一个非简单静态页面,有着较多用户交互的小程序,表单校验又是一个非常常用的功能:登录、注册、新增、编辑…

总而言之:表单组件的多样性 X 校验规则的多样性 = 复杂的公共业务

这么棘手的问题我们怎么来解决它呢?

尝试组件化

如果你关注近年前端发展趋势,一定会想到“组件化”来实现:

把每个表单组件的视图、样式、校验逻辑封装成单独的业务组件,然后直接调用。

可事情似乎没这么简单。

如果考虑把n个原生组件抽象出来,配上n个校验规则,再乘以组件之间的关系n(的全排列),复杂度至少达到n³。

而且每个组件的校验失败或成功都要通知父组件,以便显示错误信息或者进行下一步操作。

这样不但没有解决问题,反而使得这些公用的表单组件过于复杂,耦合混乱。

尝试非组件化

既然原先的思路行不通,再来回到出发点,看看我们最核心的需要被抽象出来的是什么。

无非是两样东西:视图层的元素样式和逻辑层的校验规则。

上面说到封装原生表单组件会极大的增加复杂度,索性放弃它,复杂度瞬间可以下降到n²。

但同时我们又要保持样式统一,也就是我们常说的风格一致。

比如输入框该多高,错误提示怎么显示,字体大小颜色…之类的。

这个好办,我们把样式类写入一个公共样式文件form.wxss,然后需要的时候引入,甚至可以全局引入。

// form.wxss
.form {
 display: block;
 font-size: 28rpx;
 position: relative;
}
.form-line {
 background-color: #fff;
 border-bottom: 1px solid #e5e5e5;
 font-size: 34rpx;
 height: 96rpx;
 line-height: 96rpx;
 display: flex;
 padding: 0 31rpx;
}
.form-title {
 box-sizing: border-box;
 background-color: #efefef;
 color: #838383;
 font-size: 28rpx;
 padding: 31rpx;
 min-height: 90rpx;
}
...

我们使用的时候只需要在对应的元素上添加相应的样式即可。比如:

// xxx.wxml
<form class="form">
 <view class="form-title">请输入手机号</view>
 <view class="form-line">
 <label class="label">手机</label>
 <view class="form-control">
  <input class="f-1 va-m input" />
 </view>
 </view>
 ...
</form>

那么接下来我们只剩下校验规则和组件关联关系之间这两个难题了。

校验规则理想的状态是可扩展和可配置。

可扩展。随着业务的增长,在不修改已有规则情况可以新增校验规则。

可配置。可单独为每个表单组件配置不同的单个或多个校验规则。

如何做到可定义?用统一的形式即可。比如:

/*
统一的格式:
[规则名]: {
 rule: [校验方式]
 msg: [错误信息]
}
*/
const validators = {
 // 简单的校验用正则
 required: {
 rule: /.+/,
 msg: '必填项不能为空'
 },
 // 复杂的校验用函数
 same: {
 rule (val='', sVal='') {
  return val===this.data[sVal]
 },
 msg: '密码不一致'
 }
 ...
}

如何做到可配置?配置上支持类似数组的形式,然后用统一的函数依次读取这些校验规则,逐个校验。

配置的规则肯定是在原生表单组件上,至于组件的值也只能通过事件对象获取。

如果直接绑定事件进行校验会阻碍父页面获取值,所以最好由父页面绑定事件传值,并且传入事件对象和执行环境进行处理:

/*
校验函数部分代码
e 事件对象
context 页面对象函数执行的上下文环境
*/
let validate = (e, context) => {
 // 从事件对象中获取组件的值
 let value = (e.detail.value || '').trim()
 // 从事件中获取校验规则名称
 let validator = e.currentTarget.dataset.validator ? e.currentTarget.dataset.validator .split(',') : []
 // 遍历规则进行校验
 for (let i = 0; i < validator.length; i++) {
 let ruleName = validator[i].split('=')[0]
 let ruleValue = validator[i].split('=')[1]
 let rule = validators[ruleName].rule || /.*/
 if ('function' === typeof rule) {
  rule.call(context, value, ruleValue) ? '' : validators[ruleName].msg
 } else {
  rule.test(value) ? '' : validators[ruleName].msg
 }
 }
 ...
}

调用起来也非常简单,按照固定的格式加上对应的样式,配置校验规则,然后调用校验函数。

// 部分代码示例
// page.wxml
<form>
 <!-- 一个表单组件 -->
 <view class="form-line">
 <label class="label">授权手机</label>
 <view class="form-control">
  <!-- 校验规则:必须填写,且为电话号码 -->
  <input maxlength="11" class="f-1 va-m input" bindblur="validate" type="number" data-name="phone" data-validator="required,phone" confirm-type="next" value="{{phone}}" />
  <!-- 错误图标 -->
  <icon wx:if="{{form.phone!==undefined}}" type="{{form.phone?'warn':'success'}}" size="16" />
 </view>
 </view>
 ...
</form>
// page.js
valid(e) {
 this.setData({
 [e.currentTarget.dataset.name]: e.detail.value
 })
 validate(e, this)
}

上面的代码中省略了校验错误提示和非空校验。详细代码请查看GitHub仓库:

https://github.com/yalishizhude/miniprogram-seed.git

总结

写代码最然总是要抱着最美好的想法,但同时也要做着最坏的打算。尤其是面对一些底层框架限制的时候。

面对这种情况,我们要从核心需求出发,把能抽出公用的东西都出来,同时保证可配置、可扩展。

好的的架构师不但喜欢未开垦的处女地,也应不惧布满杂石乱草的荒野~

欢迎到评论区留言交流:https://github.com/yalishizhude/webclub-discuss/issues/1

为大家推荐现在关注度比较高的微信小程序教程一篇:《微信小程序开发教程》小编为大家精心整理的,希望喜欢。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 判断 enter 事件
Feb 12 Javascript
用javascript获取当页面上鼠标光标位置和触发事件的对象的代码
Dec 09 Javascript
js检测浏览器版本、核心、是否移动端示例
Apr 24 Javascript
jQuery实现点击表格单元格就可以编辑内容的方法【测试可用】
Aug 01 Javascript
深入理解JS继承和原型链的问题
Dec 17 Javascript
jQuery插件HighCharts实现的2D面积图效果示例【附demo源码下载】
Mar 15 Javascript
JS实现图片预加载之无序预加载功能代码
May 12 Javascript
Angular 2父子组件数据传递之局部变量获取子组件其他成员
Jul 04 Javascript
使用Electron构建React+Webpack桌面应用的方法
Dec 15 Javascript
Vue2.0 实现歌手列表滚动及右侧快速入口功能
Aug 08 Javascript
IE9 elementUI文件上传的问题解决
Oct 17 Javascript
JavaScript实现H5接金币功能(实例代码)
Feb 22 Javascript
Angularjs中ng-repeat的简单实例
Aug 25 #Javascript
微信小程序页面滑动屏幕加载数据效果
Nov 16 #Javascript
Angularjs中数据绑定的实例详解
Aug 25 #Javascript
vue2利用Bus.js如何实现非父子组件通信详解
Aug 25 #Javascript
JavaScript中防止微信浏览器被整体拖动的方法
Aug 25 #Javascript
在Js页面通过POST传递参数跳转到新页面详解
Aug 25 #Javascript
Vue上传组件vue Simple Uploader的用法示例
Aug 25 #Javascript
You might like
在“咖啡之国”感受咖啡文化
2021/03/03 咖啡文化
透析PHP的配置文件php.ini
2006/10/09 PHP
PHP foreach遍历多维数组实现方式
2016/11/16 PHP
php基于Redis消息队列实现的消息推送的方法
2018/11/28 PHP
Yii框架自定义数据库操作组件示例
2019/11/11 PHP
JS 学习笔记 防止发生命名冲突
2009/07/30 Javascript
js获取单选框或复选框值及操作
2012/12/18 Javascript
jQuery+css实现图片滚动效果(附源码)
2013/03/18 Javascript
使用JSLint提高JS代码质量方法分享
2013/12/16 Javascript
js用闭包遍历树状数组的方法
2014/03/19 Javascript
AngularJS入门知识之MVW类框架的编程思想探讨
2014/12/08 Javascript
对JavaScript的全文搜索实现相关度评分的功能的方法
2015/06/24 Javascript
JavaScript运动减速效果实例分析
2015/08/04 Javascript
AngularJS控制器controller给模型数据赋初始值的方法
2017/01/04 Javascript
AngularJS实现根据不同条件显示不同控件
2017/04/20 Javascript
js用类封装pop弹窗组件
2017/10/08 Javascript
微信小程序中的canvas 文字断行和省略号显示功能的处理方法
2018/11/14 Javascript
浅谈JS和jQuery的区别
2019/03/27 jQuery
Vue基于iview实现登录密码的显示与隐藏功能
2020/03/06 Javascript
vue-preview动态获取图片宽高并增加旋转功能的实现
2020/07/29 Javascript
vue中解决chrome浏览器自动播放音频和MP3语音打包到线上的实现方法
2020/10/09 Javascript
[03:48]2014DOTA2 TI专访71DK夺冠不靠小组赛高排名
2014/07/11 DOTA
Python实现的二维码生成小软件
2014/07/11 Python
tensorflow实现简单的卷积网络
2018/05/24 Python
python 字典修改键(key)的几种方法
2018/08/10 Python
python pygame实现挡板弹球游戏
2019/11/25 Python
利用matplotlib实现根据实时数据动态更新图形
2019/12/13 Python
浅谈python print(xx, flush = True) 全网最清晰的解释
2020/02/21 Python
香港钟表珠宝首饰商城:OneMallTime网摩间
2016/10/14 全球购物
Juicy Couture Beauty官方网站:香水和化妆品
2019/03/12 全球购物
捷科时代的软件测试笔试题
2015/11/09 面试题
学习交流会主持词
2014/04/01 职场文书
党员批评与自我批评总结
2014/10/15 职场文书
公司欠款证明
2015/06/24 职场文书
公司酒会主持词
2015/07/02 职场文书
2016年国培心得体会及反思
2016/01/13 职场文书