优雅的表单验证模式--策略设计模式和ES6的Proxy代理模式
来源:互联网 发布:java synchronized原理 编辑:程序博客网 时间:2024/06/08 18:34
转载自 作者 @jawil 原文,原文有删改
网站的交互,离不开表单的提交,而一个健壮的表单离不开对表单内容的校验。
假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。
- 所有选项不能为空
- 用户名长度不能少于6位
- 密码长度不能少于6位
- 手机号码必须符合格式
- 邮箱地址必须符合格式
一般情况下,我们都会选择一种较为传统的方式,对表单内容进行校验,假设表单结构如下:
<form action="http://xxx.com/register" id="registerForm" method="post"> <div class="form-group"> <label for="user">请输入用户名:</label> <input type="text" class="form-control" id="user" name="userName"> </div> <div class="form-group"> <label for="pwd">请输入密码:</label> <input type="password" class="form-control" id="pwd" name="passWord"> </div> <div class="form-group"> <label for="phone">请输入手机号码:</label> <input type="tel" class="form-control" id="phone" name="phoneNumber"> </div> <div class="form-group"> <label for="email">请输入邮箱:</label> <input type="text" class="form-control" id="email" name="emailAddress"> </div> <button type="button" class="btn btn-default">Submit</button> </form>
传统模式
对应的 JavaScript
校验规则如下:
// registerForm为所要提交表单的 idlet registerForm = document.querySelector('#registerForm')registerForm.addEventListener('submit', function() { if (registerForm.userName.value === '') { alert('用户名不能为空!') return false } if (registerForm.userName.length < 6) { alert('用户名长度不能少于6位!') return false } if (registerForm.passWord.value === '') { alert('密码不能为空!') return false } if (registerForm.passWord.value.length < 6) { alert('密码长度不能少于6位!') return false } if (registerForm.phoneNumber.value === '') { alert('手机号码不能为空!') return false } if (!/^1(3|5|7|8|9)[0-9]{9}$/.test(registerForm.phoneNumber.value)) { alert('手机号码格式不正确!') return false } if (registerForm.emailAddress.value === '') { alert('邮箱地址不能为空!') return false } if (!/^\w+([+-.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* $/.test(registerForm.emailAddress.value)) { alert('邮箱地址格式不正确!') return false }}, false)
这样编写代码,的确能够完成业务的需求,能够完成表单的验证,但是存在很多问题,比如:
registerForm.addEventListener
绑定的函数比较庞大,包含了很多的if-else语句,看着都恶心,这些语句需要覆盖所有的校验规则。registerForm.addEventListener
绑定的函数缺乏弹性,如果增加了一种新的校验规则,或者想要把密码的长度校验从6改成8,我们都必须深入registerForm.addEventListener绑定的函数的内部实现,这是违反了开放-封闭原则的。算法的复用性差,如果程序中增加了另一个表单,这个表单也需要进行一些类似的校验,那我们很可能将这些校验逻辑复制得漫天遍野。
策略模式
策略模式的组成:
- 抽象策略角色:策略类,通常由一个接口或者抽象类实现。
- 具体策略角色:包装了相关的算法和行为。
- 环境角色:持有一个策略类的引用,最终给客户端用的。
- 具体策略角色——编写策略类
const strategies = { isNonEmpty(value, errorMsg) { return value === '' ? errorMsg : void 0 }, minLength(value, length, errorMsg) { return value.length < length ? errorMsg : void 0 }, isMoblie(value, errorMsg) { return !/^1(3|5|7|8|9)[0-9]{9}$/.test(value) ? errorMsg : void 0 }, isEmail(value, errorMsg) { return !/^\w+([+-.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value) ? errorMsg : void 0 } }
- 抽象策略角色——编写Validator类
class Validator { constructor() { this.cache = [] //保存校验规则 } add(dom, rules) { for (let rule of rules) { let strategyAry = rule.strategy.split(':') //例如['minLength',6] let errorMsg = rule.errorMsg //'用户名不能为空' this.cache.push(() => { let strategy = strategyAry.shift() //用户挑选的strategy strategyAry.unshift(dom.value) //把input的value添加进参数列表 strategyAry.push(errorMsg) //把errorMsg添加进参数列表,[dom.value,6,errorMsg] return strategies[strategy].apply(dom, strategyAry) }) } } start() { for (let validatorFunc of this.cache) { let errorMsg = validatorFunc()//开始校验,并取得校验后的返回信息 if (errorMsg) {//r如果有确切返回值,说明校验没有通过 return errorMsg } } }}
- 环境角色——客户端调用代码
let registerForm = document.querySelector('#registerForm')const validatorFunc = () => { let validator = new Validator() validator.add(registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: '用户名不能为空!' }, { strategy: 'minLength:6', errorMsg: '用户名长度不能小于6位!' }]) validator.add(registerForm.passWord, [{ strategy: 'isNonEmpty', errorMsg: '密码不能为空!' }, { strategy: 'minLength:', errorMsg: '密码长度不能小于6位!' }]) validator.add(registerForm.phoneNumber, [{ strategy: 'isNonEmpty', errorMsg: '手机号码不能为空!' }, { strategy: 'isMoblie', errorMsg: '手机号码格式不正确!' }]) validator.add(registerForm.emailAddress, [{ strategy: 'isNonEmpty', errorMsg: '邮箱地址不能为空!' }, { strategy: 'isEmail', errorMsg: '邮箱地址格式不正确!' }]) let errorMsg = validator.start() return errorMsg}registerForm.addEventListener('submit', function(e) { let errorMsg = validatorFunc() if (errorMsg) { // 注意,这里阻止表单提交,应该是通过阻止默认事件的方式, // 而 `return false;` 或者 `return;` 都是没什么卵用的 e.preventDefault() alert(errorMsg) }})
Proxy代理模式
- 利用proxy拦截不符合要求的数据:
let validator = (target, validator, errorMsg)=> { return new Proxy(target, { _validator: validator, set(target, key, value, proxy) { if(value === '') { alert(`${errorMsg[key]}不能为空!`) return target[key] = false } let va = this._validator[key] if(!!va(value)) { return Reflect.set(target, key, value, proxy) } else { alert(`${errorMsg[key]}格式不正确`) return target[key] = false } } }) }
- 负责校验的逻辑代码:
const validators = { name(value) { return value.length > 6 }, password(value) { return value.length > 6 }, mobile(value) { return /^1(3|5|7|8|9)[0-9]{9}$/.test(value) }, email(value) { return /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/.test(value) } }
- 客户端调用代码:
const errorMsg = { name: '用户名', password: '密码', mobile: '手机号码', email: '邮箱地址' } const vali = validator({}, validators, errorMsg) let registerForm = document.querySelector('#registerForm') registerForm.addEventListener('submit', (e)=>{ let validatorNext = function* (){ yield vali.name = registerForm.userName.value yield vali.password = registerForm.passWord.value yield vali.mobile = registerForm.phoneNumber.value yield vali.email = registerForm.emailAddress.value } let validator = validatorNext() validator.next() let s = vali.name && validator.next() //上一步的校验通过才执行下一步 s = s ? vali.password && validator.next() : s s = s ? vali.mobile && validator.next() : s s = s ? vali.email && validator.next() : s // 如果验证不通过,阻止表单提交 !s && e.preventDefault() })
阅读全文
0 0
- 优雅的表单验证模式--策略设计模式和ES6的Proxy代理模式
- 代理模式和策略模式的区别
- Proxy--Android的设计模式之代理模式的讲解
- Proxy代理设计模式
- 设计模式 - 代理(Proxy)
- 代理设计模式--Proxy
- Proxy(代理)设计模式
- 代理 proxy 设计模式
- 设计模式----Proxy(代理)模式
- 设计模式--代理模式(Proxy)
- 设计模式 -- 代理模式(Proxy)
- 设计模式--代理模式(Proxy)
- [设计模式]Proxy代理模式
- 设计模式--Proxy(代理模式)
- [设计模式]代理模式(Proxy)
- 设计模式-Proxy代理模式
- 设计模式--proxy代理模式
- 设计模式 -- 代理模式 Proxy
- Romantic(扩展欧几里得求逆元(裸题))
- bootstrapValidator + Ajax表单验证
- 深入浅出-iOS函数式编程的实现 && 响应式编程概念
- python 单个字节到单个字符的转换
- Opencv 检测线
- 优雅的表单验证模式--策略设计模式和ES6的Proxy代理模式
- openwrt下的wget注意要点
- 队列基本用法
- 机器学习知识点(二十五)Java实现隐马尔科夫模型HMM之jahmm库
- 机器学习感悟1
- ReactiveCocoa学习篇
- 使用HttpURLConnection和HttpClient的区别
- 爱测未来移动-QNX性能监控方案
- JQuery文字轮播简单代码