avalon2
Version:
an elegant efficient express mvvm framework
197 lines (188 loc) • 7.48 kB
JavaScript
import { avalon,isObject, platform} from '../seed/core'
var valiDir = avalon.directive('validate', {
diff: function (validator) {
var vdom = this.node
if (vdom.validator ) {
return
}
if (isObject(validator)) {
//注意,这个Form标签的虚拟DOM有两个验证对象
//一个是vmValidator,它是用户VM上的那个原始子对象,也是一个VM
//一个是validator,它是vmValidator.$model, 这是为了防止IE6-8添加子属性时添加的hack
//也可以称之为safeValidate
vdom.vmValidator = validator
validator = platform.toJson(validator)
vdom.validator = validator
for (var name in valiDir.defaults) {
if (!validator.hasOwnProperty(name)) {
validator[name] = valiDir.defaults[name]
}
}
validator.fields = validator.fields || []
return true
}
},
update: function (vdom) {
var validator = vdom.validator
var dom = vdom.dom
validator.dom = dom
dom._ms_validate_ = validator
//为了方便用户手动执行验证,我们需要为原始vmValidate上添加一个onManual方法
var v = vdom.vmValidator
try {
v.onManual = onManual
} catch (e) {
}
delete vdom.vmValidator
dom.setAttribute('novalidate', 'novalidate')
function onManual() {
valiDir.validateAll.call(validator, validator.onValidateAll)
}
/* istanbul ignore if */
if (validator.validateAllInSubmit) {
avalon.bind(dom, 'submit', function (e) {
e.preventDefault()
onManual()
})
}
/* istanbul ignore if */
if (typeof validator.onInit === 'function') { //vmodels是不包括vmodel的
validator.onInit.call(dom, {
type: 'init',
target: dom,
validator: validator
})
}
},
validateAll: function (callback) {
var validator = this
var fn = typeof callback === 'function' ? callback : validator.onValidateAll
var promises = validator.fields.filter(function (field) {
var el = field.dom
return el && !el.disabled && validator.dom.contains(el)
}).map(function (field) {
return valiDir.validate(field, true)
})
var uniq = {}
return Promise.all(promises).then(function (array) {
var reasons = array.concat.apply([], array)
if (validator.deduplicateInValidateAll) {
reasons = reasons.filter(function (reason) {
var el = reason.element
var uuid = el.uniqueID || (el.uniqueID = setTimeout('1'))
if (uniq[uuid]) {
return false
} else {
return uniq[uuid] = true
}
})
}
fn.call(validator.dom, reasons) //这里只放置未通过验证的组件
})
},
addField: function (field) {
var validator = this
var node = field.dom
/* istanbul ignore if */
if (validator.validateInKeyup && (!field.isChanged && !field.debounceTime)) {
avalon.bind(node, 'keyup', function (e) {
validator.validate(field, 0, e)
})
}
/* istanbul ignore if */
if (validator.validateInBlur) {
avalon.bind(node, 'blur', function (e) {
validator.validate(field, 0, e)
})
}
/* istanbul ignore if */
if (validator.resetInFocus) {
avalon.bind(node, 'focus', function (e) {
validator.onReset.call(node, e, field)
})
}
},
validate: function (field, isValidateAll, event) {
var promises = []
var value = field.value
var elem = field.dom
/* istanbul ignore if */
if (typeof Promise !== 'function') {//avalon-promise不支持phantomjs
avalon.wain('please npm install es6-promise or bluebird')
}
/* istanbul ignore if */
if (elem.disabled)
return
var rules = field.rules
var ngs = [], isOk = true
if (!(rules.norequired && value === '')) {
for (var ruleName in rules) {
var ruleValue = rules[ruleName]
if (ruleValue === false)
continue
var hook = avalon.validators[ruleName]
var resolve
promises.push(new Promise(function (a, b) {
resolve = a
}))
var next = function (a) {
var reason = {
element: elem,
data: field.data,
message: elem.getAttribute('data-' + ruleName + '-message') || elem.getAttribute('data-message') || hook.message,
validateRule: ruleName,
getMessage: getMessage
}
if (a) {
resolve(true)
} else {
isOk = false
ngs.push(reason)
resolve(false)
}
}
field.data = {}
field.data[ruleName] = ruleValue
hook.get(value, field, next)
}
}
//如果promises不为空,说明经过验证拦截器
return Promise.all(promises).then(function (array) {
if (!isValidateAll) {
var validator = field.validator
if (isOk) {
validator.onSuccess.call(elem, [{
data: field.data,
element: elem
}], event)
} else {
validator.onError.call(elem, ngs, event)
}
validator.onComplete.call(elem, ngs, event)
}
return ngs
})
}
})
var rformat = /\\?{{([^{}]+)\}}/gm
function getMessage() {
var data = this.data || {}
return this.message.replace(rformat, function (_, name) {
return data[name] == null ? '' : data[name]
})
}
valiDir.defaults = {
validate: valiDir.validate,
addField: valiDir.addField, //供内部使用,收集此元素底下的所有ms-duplex的域对象
onError: avalon.noop,
onSuccess: avalon.noop,
onComplete: avalon.noop,
onManual: avalon.noop,
onReset: avalon.noop,
onValidateAll: avalon.noop,
validateInBlur: true, //@config {Boolean} true,在blur事件中进行验证,触发onSuccess, onError, onComplete回调
validateInKeyup: true, //@config {Boolean} true,在keyup事件中进行验证,触发onSuccess, onError, onComplete回调
validateAllInSubmit: true, //@config {Boolean} true,在submit事件中执行onValidateAll回调
resetInFocus: true, //@config {Boolean} true,在focus事件中执行onReset回调,
deduplicateInValidateAll: false //@config {Boolean} false,在validateAll回调中对reason数组根据元素节点进行去重
}