用vue寫輪子的一些心得(七)——表單校驗組件

 

需求分析

  • 支持內置校驗器,比如:郵箱校驗、手機號校驗等等;
  • 支持自定義校驗器;
  • 支持自定義校驗器,並不會造成全局污染,每次new一個新實例;

 

方法實現

1、表單校驗基礎搭建:

校驗器組件邏輯,首先校驗必填項,required: true 是否必填,若無填寫則直接return報錯「必填」。

接着再校驗其它內置規則,email、手機號等等。

export default function validate (data, rules) {
    let errors = {}
    rules.forEach(rule => {
        let value = data[rule.key]
        if (rule.required) {
            if (value !== 0 && !value) {
                errors[rule.key] = {required: '必填'}
                return
            }
        }
        if (rule.pattern) {
            if (rule.pattern === 'email') {
                rule.pattern = /^.+@.+$/
            }
            if (rule.pattern.test(value) === false) {
                ensureObject(errors, rule.key)
                errors[rule.key] = {pattern: '格式不正確'}
            }
        }
        if (rule.minLength) {
            if (value.length < rule.minLength) {
                ensureObject(errors, rule.key)
                errors[rule.key].minLength = '太短'
            }
        }
    })
    return errors
}

function ensureObject (obj, key) {
    if (typeof obj[key] !== 'object') {
        obj[key] = {}
    }
}

用法:

let data = {email: ''}
let rules = [{key: 'email', required: true}]
let errors = validate(data, rules)

 2、可將表單校驗if邏輯抽離出來:

遍歷validators,通過對象key: value 的形式,可在外部自定義校驗規則。

export default function validate (data, rules) {
    let errors = {}
    rules.forEach(rule => {
        let value = data[rule.key]
        if (rule.required) {
            let error = validate.required(value)
            if (error) {
                ensureObject(errors, rule.key)
                errors[rule.key].required = error
                return
            }
        }
        // 遍歷validators,並逐一調用對應的函數
        let validators = Object.keys(rule).filter(key => key !== 'key' && key !== 'required')
        validators.forEach(item => {
            let error = validate[item] && validate[item](value, rule[item])
            if (error) {
                ensureObject(errors, rule.key)
                errors[rule.key][item] = error
            }
        })
    })
    return errors
}

validate.required = (value) => {
    if (value !== 0 && !value) {
        return '必填'
    }
}
validate.pattern = (value, pattern) => {
    if (value !== 'email') {
        pattern = /^.+@.+$/
    }
    if (pattern.test(value) === false) {
        return '格式不正確'
    }
}
validate.minLength = (value, pattern) => {
    if (value.length < pattern) {
        return '太短'
    }
}

function ensureObject (obj, key) {
    if (typeof obj[key] !== 'object') {
        obj[key] = {}
    }
}

 

3、改寫爲面向對象方法

每次可new一個新實例,這樣好處是並不會造成全局污染。

另外我們可在class中增加一個靜態方法 add,用戶可自行選擇將自定義校驗規則添加至新實例上(不共享),或是Validator原型上(共享)。

class Validator {
    static add (name, fn) { //靜態方法
        Validator.prototype[name] = fn
    }
    constructor() {}

    validate(data, rules) {
        let errors = {}
        rules.forEach(rule => {
            let value = data[rule.key]
            if (rule.required) {
                let error = this.required(value)
                if (error) {
                    this.ensureObject(errors, rule.key)
                    errors[rule.key].required = error
                    return
                }
            }
            // 遍歷validators,並逐一調用對應的函數
            let validators = Object.keys(rule).filter(key => key !== 'key' && key !== 'required')
            validators.forEach(item => {
                if (this[item]) {
                    let error = this[item] && this[item](value, rule[item])
                    if (error) {
                        this.ensureObject(errors, rule.key)
                        errors[rule.key][item] = error
                    }
                } else {
                    throw `不存在的校驗器:${item}`
                }
            })
        })
        return errors
    }

    required(value) {
        if (value !== 0 && !value) {
            return '必填'
        }
    }

    pattern(value, pattern) {
        if (value !== 'email') {
            pattern = /^.+@.+$/
        }
        if (pattern.test(value) === false) {
            return '格式不正確'
        }
    }

    minLength(value, pattern) {
        if (value.length < pattern) {
            return '太短'
        }
    }

    ensureObject(obj, key) {
        if (typeof obj[key] !== 'object') {
            obj[key] = {}
        }
    }
}

export default Validator

4、測試用例:

import chai, {expect} from 'chai'
import sinon from 'sinon'
import sinonChai from 'sinon-chai'
chai.use(sinonChai)

import Validator from '../../src/validate'

describe('Validator', ()=> {
    it('存在', ()=> {
        expect(Validator).to.exist
    })
    it('required true 報錯', ()=> {
        let data = {
            email: ''
        }
        let rules = [
            {key: 'email', required: true}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email.required).to.eq('必填')
    })
    it('required true 通過', ()=> {
        let data = {
            email: 0
        }
        let rules = [
            {key: 'email', required: true}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email).to.not.exist
    })
    it('attern 正則 報錯', ()=> {
        let data = {
            email: '@go.com'
        }
        let rules = [
            {key: 'email', pattern: /^.+@.+$/}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email.pattern).to.eq('格式不正確')
    })
    it('pattern 正則 通過', ()=> {
        let data = {
            email: '[email protected]'
        }
        let rules = [
            {key: 'email', pattern: /^.+@.+$/}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email).to.not.exist
    })
    it('pattern email 報錯', ()=> {
        let data = {
            email: '@go.com'
        }
        let rules = [
            {key: 'email', pattern: 'email'}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email.pattern).to.eq('格式不正確')
    })
    it('pattern email 通過', ()=> {
        let data = {
            email: '[email protected]'
        }
        let rules = [
            {key: 'email', pattern: 'email'}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email).to.not.exist
    })
    it('required & pattern', ()=> {
        let data = {
            email: ''
        }
        let rules = [
            {key: 'email', pattern: 'email', required: true}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email.required).to.exist
        expect(errors.email.pattern).to.not.exist
    })
    it('pattern & minLength', ()=> {
        let data = {
            email: ''
        }
        let rules = [
            {key: 'email', pattern: 'email', minLength: 6}
        ]
        let validator = new Validator()
        let errors = validator.validate(data, rules)
        expect(errors.email.minLength).to.exist
        expect(errors.email.pattern).to.exist
    })
    it('自定義測試規則 hasNumber', ()=> {
        let data = {
            email: 'asdfsdf'
        }
        let validator = new Validator()
        validator.hasNumber = (value) => {
            if (!/\d/.test(value)) {
                return '必須含有數字'
            }
        }
        let rules = [{key: 'email', required: true, hasNumber: true}]
        let errors
        let fn = () => {
            errors = validator.validate(data, rules)
        }
        expect(fn).to.not.throw()
        expect(errors.email.hasNumber).to.eq('必須含有數字')
    })

})

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章