Skip to content

Instantly share code, notes, and snippets.

@TsubasaKawajiri
Last active August 12, 2020 11:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TsubasaKawajiri/f3fbd029119de87c208535bc1c0cca2b to your computer and use it in GitHub Desktop.
Save TsubasaKawajiri/f3fbd029119de87c208535bc1c0cca2b to your computer and use it in GitHub Desktop.
JavaScript versatile Form Validator. inspired ActiveModelValidations
import ValidatorsFactory from '../validators/ValidatorsFactory'
const pagename = /* get pagename */
const factory = new ValidatorsFactory(pageName)
const validators = factory.createValidators()
if (validators === null) {
return
}
validators.bindSubmitButton()
validators.forEach((validator) => {
validator.bind()
})
export default class AbstractValidator {
bind(){}
_validate(){}
backgroundValidate(){
this._resetError()
this._validate()
}
foregroundValidate(){
this._resetError()
this._hideError()
this._validate()
if (this.hasError()) {
this._showError()
}
}
hasError(){}
_showError(){}
_hideError(){}
_resetError(){}
_buildErrorHTML(){}
_presence(){}
_maxLength(){}
_minLength(){}
_betweenLength(){}
_regex(){}
}
export class TextValidator extends AbstractValidator {
constructor(target){
super()
// input name. example: last_name
this.name = target.name
this.input = target.input
this.validation = target.validation
this.error = ''
}
bind(){
this.input.blur(() => {
this.foregroundValidate()
})
}
hasError(){
return this.error != ''
}
_validate(){
this.val = this.getValue()
this._presence()
if (this.validation.betweenLength) {
this._betweenLength()
} else {
this._minLength()
this._maxLength()
}
this._regex()
}
_showError(){
this.input.parent.append(this._buildErrorHTML(this.error))
}
_hideError(){
this.input.parent.children('.error').remove()
}
_resetError(){
this.error = ''
}
_buildErrorHTML(error){
return `<span class='error'>${error}</span>`
}
// validations
_presence(){
if (!this.validation.presence) {
return
}
if(this.presenceCondition()){
this.error = this.presenceErrorText()
}
}
_minLength(){
if (this.validation.minLength < 0 || this.error) {
return
}
if(this.val.length < this.validation.minLength){
this.error = this.minLengthErrorText()
}
}
_maxLength(){
if (this.validation.maxLength <= 0 || this.error) {
return
}
if(this.val.length > this.validation.maxLength){
this.error = this.maxLengthErrorText()
}
}
_betweenLength(){
if (this.validation.minLength <= 0 || 0 >= this.validation.maxLength && this.error) {
return
}
if (this.val.length < this.validation.minLength || this.validation.maxLength < this.val.length) {
this.error = this.betweenLengthErrorText()
}
}
_regex(){
if (!this.validation.regex || this.error) {
return
}
if (!this.val.match(this.validation.regex)) {
this.error = this.validation.regexErrorText
}
}
// for extends
/**
* @return {value} InputValue
*/
getValue(){
return this.input.val()
}
/**
* @return {boolean}
*/
presenceCondition(){
return this.val === '' || this.val === undefined || this.val === null
}
/**
* @return {string}
*/
presenceErrorText(){
return `${this.name ? this.name + 'is' : ''} must input`
}
/**
* @return {string}
*/
minLengthErrorText(){
return `${this.name ? this.name + 'is' : ''} must more than ${this.validation.minLength}`
}
/**
* @return {string}
*/
maxLengthErrorText(){
return `${this.name ? this.name + 'is' : ''} must less than ${this.validation.maxLength}`
}
/**
* @return {string}
*/
betweenLengthErrorText(){
return `${this.name ? this.name + 'is' : ''} between more than${this.validation.minLength} and less than ${this.validation.maxLength}`
}
}
const ValidationTargets = {
sample: [
{
name: 'mail_address', input: /* input element */,
validation:{
presence: true, maxLength: 100,
regex: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(?!\.).@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*/,
regexErrorText: 'invalid',
}, type: 'text'
},
{
name: 'last_name', form: /* input element */,
validation: { presence: true, maxLength: 30 }, type: 'text'
}
]
}
export default ValidationTargets
export class Validators {
constructor(form){
this.validators = []
this.form = form
}
bindSubmitButton(){
let isSubmitting = false
this.form.submit(() => {
if(isSubmitting) {
return false
}
if(this._canSubmit()) {
isSubmitting = true
return true
}
})
}
push(validator){
this.validators.push(validator)
}
forEach(callback){
return this.validators.forEach(callback)
}
filter(callback){
return this.validators.filter(callback)
}
_canSubmit(){
this.validators.forEach(validator => {
validator.foregroundValidate()
})
return this.validators.every((validator => {
return !validator.hasError()
}))
}
}
import ValidationTargets from './ValidationTargets'
import TextValidator from './Validator'
export default class ValidatorsFactory {
constructor(pageName){
this.pageName = pageName
}
createValidators(){
const validators = new Validators()
ValidationTargets[this.pageName].forEach((target) => {
validators.push(this._validator(target))
})
return validators
}
_validator(target){
if (target.type === 'text') {
return new TextValidator(target)
}
// Example Sample
// if(target.type === 'password') {
// return new PasswordValidator(target)
// }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment