Created
June 5, 2017 02:30
-
-
Save jiraiyame/339261407c5ed2236a37d991009ced11 to your computer and use it in GitHub Desktop.
form validation component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { event, Emitter, classes, query, validation, lang } from 'lib'; | |
let _uid = 0; | |
class Form extends Emitter { | |
constructor(options) { | |
super(); | |
if (!(this instanceof Form)) { | |
return new Form(options); | |
} | |
this.fields = {}; | |
this.options = Object.assign({}, Form.defaultOptions, options); | |
return this; | |
} | |
init() { | |
const form = query(this.options.container); | |
if (lang.isBlank(form)) { | |
return; | |
} | |
// Turn off default validation by the browser | |
form.setAttribute('novalidate', true); | |
const inputs = query.all('input', form); | |
[...inputs].forEach(input => this.bind(input)); | |
this.on('change', (res, field) => { | |
field.valid = res.valid; | |
field.message = res.message; | |
if (!res.valid) { | |
res.input.focus(); | |
} | |
const isCheckbox = res.input.type === 'checkbox'; | |
this.render(res, !isCheckbox); | |
this.checkFormValidity(); | |
}); | |
this.checkFormValidity(); | |
this.form = form; | |
} | |
checkFormValidity() { | |
const isValid = Object.keys(this.fields).every(key => this.fields[key].valid); | |
const buttons = query.all('button', this.form); | |
[...buttons].filter(button => button.type === 'submit').forEach(button => { | |
button.disabled = !isValid; | |
}); | |
return isValid; | |
} | |
bind(input) { | |
let valid; | |
if (['checkbox', 'radio'].indexOf(input.type) !== -1) { | |
valid = input.checked; | |
} else { | |
valid = !!input.value; | |
} | |
input._uid = _uid++; | |
this.fields[input._uid] = { | |
valid: valid ? valid : !input.required, | |
message: null, | |
}; | |
event.on(input, 'keyup change', (e) => this.checkValidity(e.target)); | |
} | |
checkValidity(input) { | |
const ret = { | |
input, | |
valid: true, | |
message: null, | |
}; | |
const broadcast = (res) => { | |
this.field = res; | |
this.emit('change', Object.assign({}, ret, res), this.fields[input._uid]); | |
}; | |
let proxy = validation.field(input, 'valueMissing'); | |
return proxy.then(res => { | |
if (input.required) { | |
broadcast(res); | |
} | |
if (res.valid) { | |
if (/^(email|url|tel|password)$/.test(input.type)) { | |
proxy = validation[input.type](input); | |
} else if (input.pattern) { | |
proxy = validation.field(input, 'patternMismatch'); | |
} | |
proxy.then(res => broadcast(res)).catch(err => console.error(err)); | |
} | |
return Promise.resolve(proxy); | |
}).catch(err => console.error(err)); | |
} | |
renderMessage(message) { | |
const { prefix } = this.options; | |
const messageClass = `${prefix}validate-message`; | |
let errorNode = query(`.${messageClass}`, this.form); | |
if (!errorNode) { | |
errorNode = document.createElement('div'); | |
errorNode.className = messageClass; | |
const fields = query.all('.form-field'); | |
const lastField = fields[fields.length - 1]; | |
lastField.parentNode.insertBefore(errorNode, lastField.nextSibling); | |
} | |
errorNode.style.display = message ? 'block' : 'none'; | |
errorNode.textContent = message; | |
} | |
findField(input) { | |
const field = input.parentNode; | |
field.cx = field.classList; | |
return field; | |
} | |
render(res, renderStatus = true) { | |
const field = this.findField(res.input); | |
if (!field) { | |
return; | |
} | |
this.renderMessage(res.message); | |
if (renderStatus) { | |
const cx = classes(field); | |
cx.remove('valid', 'invalid').add( | |
'form-status', | |
res.valid ? 'valid' : 'invalid' | |
); | |
} | |
} | |
} | |
Form.defaultOptions = { | |
container: '.form', | |
// 类名前缀,如: '.prefix-' | |
prefix: '', | |
}; | |
export default Form; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment