使用策略模式&代理模式进行表单验证

前言

在学习 策略模式 的时候,发现可以用 策略模式&代理模式 进行表单验证,之前自己一直是使用条件语句进行判断,设计模式给自己提供了很好的思路,记录一下这两种模式的大致实现。

原先的实现

1
2
3
4
5
6
7
8
9
10
11
12
<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>

<button type="button" class="btn btn-default">Submit</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let 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
}
}, false)

这样会有很多缺点:

  1. if-else 语句会很多,这些语句需要覆盖所有的校验规则
  2. 违反 “开闭原则”,如果需要增加一种新的校验规则或者更改校验规则,需要在函数内部进行修改
  3. 复用性很差,其他表格也需要类似的校验时,可能需要各种复制粘贴

策略模式 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* 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) {// 如果有确切返回值,说明校验没有通过
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: '密码不能为空!'
}])
let errorMsg = validator.start()
return errorMsg
}

registerForm.addEventListener('submit', function() {
let errorMsg = validatorFunc()
if (errorMsg) {
alert(errorMsg)
return false
}
}, false)

代理模式 实现

策略模式 写的有点多,使用 Proxy 实现一下:(记得要装 babel 哦!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 利用proxy拦截不符合要求的数据
function validator(target, validator, errorMsg) {
return new Proxy(target, {
_validator: validator,
set(target, key, value, proxy) {
let errMsg = errorMsg
if (value == '') {
alert(`${errMsg[key]}不能为空!`)
return target[key] = false
}
let va = this._validator[key]
if (!!va(value)) {
return Reflect.set(target, key, value, proxy)
} else {
alert(`${errMsg[key]}格式不正确`)
return target[key] = false
}
}
})
}

// 负责校验的逻辑代码
const validators = {
name(value) {
return value.length > 6
},
passwd(value) {
return value.length > 6
},
moblie(value) {
return /^1(3|5|7|8|9)[0-9]{9}$/.test(value)
},
email(value) {
return /^\w+([+-.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value)
}
}

// 客户端调用代码
const errorMsg = { name: '用户名', passwd: '密码', moblie: '手机号码', email: '邮箱地址' }
const vali = validator({}, validators, errorMsg)
let registerForm = document.querySelector('#registerForm')
registerForm.addEventListener('submit', function() {
let validatorNext = function*() {
yield vali.name = registerForm.userName.value
yield vali.passwd = registerForm.passWord.value
yield vali.moblie = registerForm.phoneNumber.value
yield vali.email = registerForm.emailAddress.value
}
let validator = validatorNext()
validator.next();
!vali.name || validator.next(); //上一步的校验通过才执行下一步
!vali.passwd || validator.next();
!vali.moblie || validator.next();
}, false)