Skip to content

Instantly share code, notes, and snippets.

@jiraiyame
Created June 5, 2017 02:30
Show Gist options
  • Save jiraiyame/339261407c5ed2236a37d991009ced11 to your computer and use it in GitHub Desktop.
Save jiraiyame/339261407c5ed2236a37d991009ced11 to your computer and use it in GitHub Desktop.
form validation component
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