Skip to content

Commit 0cae606

Browse files
committed
fix(Form Validation): Fix validation bug when validatables nested
1 parent 2e5eb96 commit 0cae606

4 files changed

Lines changed: 66 additions & 8 deletions

File tree

src/components/form/form-item.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,17 @@ export default {
5858
}
5959
},
6060
created () {
61+
const isParentValidatable = v => {
62+
let parent = v.$parent
63+
while (true) {
64+
if (!parent || parent === this) return false
65+
if (parent.isValidatable) return true
66+
parent = parent.$parent
67+
}
68+
}
6169
this.$on('validatable-attached', v => {
6270
// skip child validatable if parent is validatable
63-
if (v.$parent.isValidatable) return
71+
if (isParentValidatable(v)) return
6472
this.validatable = v
6573
})
6674
this.$on('validatable-detached', v => {

src/components/form/index.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ Clair 目前支持验证的 `type` 有:
435435

436436
除了 Clair 内置的验证规则之外,你也可以自定义验证规则。只需要提供一个规则名和一个验证函数即可。验证函数的参数为用户输入的值,如果验证失败,返回 `{ valid: false, msg: '错误提示内容' }`;验证通过则返回 `{ valid: true }` 即可。
437437

438+
在自定义验证函数中,可以使用 `this` 访问触发验证的 Vue 组件实例。
439+
438440
```html
439441
设置密码:<c-input v-model="password" :rules="rules" />
440442

@@ -447,6 +449,7 @@ Clair 目前支持验证的 `type` 有:
447449
required: true,
448450
minlength: 6,
449451
strength: function (val) {
452+
console.log(this) // this 是 c-input 实例
450453
const hasNumber = /\d/.test(val)
451454
const hasLetter = /[a-z]/i.test(val)
452455
const hasSpecialChar = /\W/.test(val)
@@ -618,6 +621,52 @@ export default {
618621
</script>
619622
```
620623

624+
### 扩展表单组件
625+
626+
在复杂的业务场景下,我们需要自定义表单组件,这些组件可以使用 [`v-model` 进行双向绑定](https://cn.vuejs.org/v2/guide/components.html#%E5%9C%A8%E7%BB%84%E4%BB%B6%E4%B8%8A%E4%BD%BF%E7%94%A8-v-model)
627+
628+
Clair 也提供了一些方法,让你开发的自定义组件也可以使用 Clair 的表单验证机制。
629+
630+
```javascript
631+
import Clair from clair
632+
633+
/**
634+
* 自定义表单组件使用Clair的表单验证系统
635+
*/
636+
Vue.component('fancy-input', {
637+
mixins: [Clair.mixins.validatable],
638+
props: ['value'],
639+
data () {
640+
return {
641+
// 这里可以内置一些验证规则
642+
builtinRules: {
643+
ruleA (val) {
644+
// ...
645+
}
646+
}
647+
}
648+
}
649+
// ...
650+
})
651+
652+
/**
653+
* 使用 `rules` 属性传入验证规则
654+
*/
655+
new Vue({
656+
el: '#app',
657+
template: '<fancy-input v-model="val" :rules="rules">',
658+
data () {
659+
return {
660+
val: '',
661+
rules: {
662+
required: true,
663+
maxLength: 12
664+
}
665+
}
666+
}
667+
})
668+
```
669+
621670
## 属性说明
622671

623672
### `c-form` 属性

src/scripts/mixins/validatable/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,16 @@ export default {
8282
methods: {
8383
validate () {
8484
this.validity.dirty = true
85-
const { $formItem } = this
85+
const { $formItem, builtinRules } = this
8686
const required = $formItem && !this.$parent.isValidatable && $formItem.required
87-
const rules = Object.assign({ required }, this.rules)
87+
const rules = Object.assign({ required }, builtinRules, this.rules)
8888
if (!rules.msg) rules.msg = {}
8989
if (typeof rules.msg === 'object' && !rules.msg.required) {
9090
const label = $formItem && $formItem.label ? $formItem.label : ''
9191
const action = this.$options.name === 'c-input' ? '填写' : '选择'
9292
rules.msg.required = `请${action}${label.replace(/[:]/, '')}`
9393
}
94-
const result = Validator.validate(this.value, rules)
94+
const result = Validator.validate(this.value, rules, this)
9595
const setValidity = v => Object.assign(this.validity, v)
9696
const isLatest = id => this.validationId === id
9797
{

src/scripts/mixins/validatable/validator.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ export default { validate }
77
* 验证 value 是否符合规则
88
* @param value {String} 要验证的值
99
* @param rules {Object} 规则
10+
* @param context {Object} 触发验证的Vue组件
1011
* @return {Object} 结果对象,有valid和msg两个字段
1112
*/
12-
function validate (value, rules = {}) {
13+
function validate (value, rules = {}, context) {
1314
// msg 为自定义错误信息
1415
const { msg } = rules
1516
const pass = { valid: true }
@@ -20,7 +21,7 @@ function validate (value, rules = {}) {
2021

2122
const results = Object.keys(rules)
2223
.filter(ruleName => canValidate(ruleName, rules[ruleName]))
23-
.map(ruleName => checkSingleRule(ruleName, rules[ruleName], value, msg))
24+
.map(ruleName => checkSingleRule(ruleName, rules[ruleName], value, msg, context))
2425

2526
const hasAsync = results.some(isPromise)
2627
const getResult = results => {
@@ -35,9 +36,9 @@ function validate (value, rules = {}) {
3536
/**
3637
* 验证单条规则
3738
*/
38-
function checkSingleRule (ruleName, param, value, msg) {
39+
function checkSingleRule (ruleName, param, value, msg, context) {
3940
const validFunction = typeof param === 'function' ? param : ruleset[ruleName]
40-
const result = validFunction(value, param)
41+
const result = validFunction.call(context, value, param)
4142
if (!result.valid && msg) { // 验证不通过且有自定义消息
4243
if (typeof msg === 'string') { // 自定义消息为字符串时直接使用
4344
result.msg = msg

0 commit comments

Comments
 (0)