轻量级表单校验模块(ES6)

具备哪些能力

  1. 只需引入模块无需额外初始化,即可在onchange时对所有绑定data-rule="规则"属性的表单元素触发校验。

  2. 除内置的规则(url、email、整数和浮点数等),还允许用户注册新的“规则”(见register方法)。

  3. 允许用户重置“错误信息的展示、errorHander、successHander”等行为(通过reset接口见下文),这样设计是为了更灵活的与第三方css组件融合

  4. 在submit时,只需手动调用一个方法,并给该方法指定一个“范围”,即可得到校验结果(范围内的所有“输入”是否全都有效)

总之,要校验谁就给谁加data-rule,再简易的配置一下规则即可。

安装

<input type="text" name="link" placeholder="链接" data-rule="url" data-help="不能为空" required>
import form from './form.js'
import $ from 'jquery';

初始化完毕~,以上input在value改变时即可触发内置的url格式校验。

属性

@data-rule *

必选项,只有包含data-rule属性的表单元素才会被校验。支持两种写法:

  1. data-rule=”类型”

  2. data-rule=”类型|表达式”

关于第一种写法,所有内置类型(共7种,见下表)都支持这么写。
关于第二种写法,只有“zh,en,number,select”3种类型才支持这种写法。表达式中允许出现3个属性:size、decimal、type(后2个只能在number类型的表达式中出现)

<input type="text" name="nickname" data-rule="zh|size:6-8">
<input type="text" name="username" data-rule="eh|size:6-8">       
<input type="text" name="total" data-rule="number|size:6-8,decimal:2-4,type:-1" > 
  • szie 后面的值允许使用’-‘表示范围(6到8位中文),当然更可以只写一个单纯的数字

  • decimal 是number类型的专属,表示保留几位小数,也可以不写,表示不限制小数

  • type 是number类型的专属,值只能是’+1或-1’,分别表示正负数。也可以不写,

所有类型:

类型说明
url链接,带不带协议头均可,支持ftp(s)和http(s)
regexp正则表达式,把值写在pattern属性上,模块会取这里的值进行校验
REG_*通过register方法添加自定义的类型
email邮箱
zh中文,支持写法2
en英文,支持写法2
number数字,支持写法2,正负数、整数和浮点数

特别说明:类型为regexp时,正则表达式需要写在pattern属性上。

<input type="text" name="正则校验" data-rule="regexp" pattern="^\d{1,3}$">

@data-help

选填项,错误时error wording从这里取

@required

选填项,表示该input为必填项,不写则允许空值。比如下例允许空值,但非空时会校验输入值是否有效:

<input type="text" name="link" placeholder="链接" data-rule="url" data-help="不能为空">

方法

@regeister(object)

注册新的数据规则

import form from './form.js'
import $ from 'jquery';

//添加两种数据格式
form.register({
    REG_PASSWORD: /^[A-Z]\w{5,11}$/,
    REG_NUMBER: /^\d+\.\d{2}$/
})

@reset(type,slot)

type 字符串类型,允许出现warn、errorHandler和successHander。

  • warn表示重置错误信息的展示方式,默认是以alert方式,可以传入第三方的toptip或者toast插件。

 form.reset('warn', $.toptip)

 //或者

 var toast = function(type) {
    return function(msg) {
        $.toast(msg, type)
    }
 }
form.reset('warn', toast('forbidden'))
  • errorHander 表示校验返回失败时执行的动作,模块内会默认给重置的slot函数传入$input对象

 form.reset('errorHandler', ($input)=>{
    $input.closest('.form-group').addclass('has-error')
 }) 

以上实现了校验失败时,往第一个临近当前input的.form-group元素上添加’has-error’样式。 如果不重置,默认是添加在input上

  • successHandler 表示校验返回成功时执行的动作。用法与errorHandler相同。

注意:errorHandler和successHandler的重置是对称的,不能只重置一个。因为如果只重置errorHanlder(把.has-error挂到.form-group上),success时,默认是从input上移除.has-error的

slot 函数类型

@validate(selector)

selector 字符串类型,符合jquery语法的选择器。校验指定选择器内的所有表单元素的输入是否有效,返回true或false

完整源码

import $ from 'jquery';
/**
 *
 * 用法设计
 *
 * ```html
 *   <input type="text" name="link" placeholder="链接" data-rule="url" data-help="不能为空" required>
 * ```
 *
 *
 * @data-rule 表示该input应输入的值类型,有两种写法
 * 1. data-rule="类型"
 * - url
 * - email
 * - zh
 * - en
 * - regexp 当设置该类型时,必须跟上pattern属性,值为正则表达式
 * - REG_* 通过register方法注册的自定义类型
 *
 * 2. data-rule="类型|表达式"
 *    表达式只会出现3种属性:size、decimal、type(后两者只用于描述number类型)
 * - "en|size:3" 表示只能输入3位英文
 * - "zh|size:3-10" 表示允许输入3到10位的中文
 * - "number|size:3-10,type:'+1',decimal:2"
 *   + type 表示正数(+1),负数(-1),没有type表示正负数以及0
 *   + decimal 表示小数点保留几位,没有decimal表示整数
 *   + size 表示位数,没有表示不限位数
 * - "select|size:1-3" 表示允许选中几个,一般用于checkbox的第一个
 *
 * @data-help 报错的wording会从 data-help | placeholder 里获得
 *
 *
 * @required  选填属性。不加该属性表示该项允许空值(即非必填)
 */
let defaults = {
    //支持完整(http://www.a.com)的或者不带协议(www.a.com)的url
    //并且协议只认http(s)和ftp(s)    
    url: /^((f|ht){1}(tp|tps):\/\/)?([\w-]+\.)+[\w-]+(\/[\w- .\?%&=]*)?/,
    //@左边只允许数字、字母、'.'和'_'
    //@右边允许出现@sina.com.cn这样的格式
    email: /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/,
    //纯中文
    zh: /^[\u4e00-\u9fa5]+$/,
    //纯英文
    en: /^[a-zA-Z]+$/,
    //纯数字,支持正负数,浮点数,以及"1,300,268"这样的写法
    number: /^-?\d+(\.\d+)?$/,
    //不包含`,~,!,@,#,$,%,^,&,*,(,),+,<,>,?,:,",{,},\,/,;,',[,]的任意字符 
    // word: /[`~!@#\$%\^\&\*\(\)\+<>\?:"\{\}\\\/;'\[\]]/im, 
    regexp: '',
    select: ''
};
let successClass = 'has-success';
let errorClass = 'has-error';
let patterns = defaults;
/**
 * 展示错误信息
 * @param  {String}   msg    [错误信息]
 * @param  {Function} theWay [默认以alert方式,可以通过暴露的reset接口重置]
 * @return {[type]}          [展示错误信息]
 */
let _warn = function(msg, say = alert) {
    say(msg)
};
let _errorHandler = function($input) {
    $input.addClass(errorClass).removeClass(successClass);
};
let _successHandler = function($input) {
    $input.removeClass(errorClass).addClass(successClass);
};
/**
 * 校验input
 * @param  {Object} $input [jq对象]
 * @return {Boolean}       [true校验通过,false校验失败]
 */
let _checkIt = function($input) {
    //1、'data-rule'值为空,不校验
    //2、'data-rule'值为'regxp',但'pattern'为空,不校验
    //3、 必填项为空值时,直接提示(不再进行parseRule)
    //4、非比填项允许为空值
    //5、data-rule和input value都不为空时,不管有没required都得校验
    if (!$input.data('rule') || ($input.data('rule') == 'regexp' && !$input.prop('pattern'))) {
        return false
    } else if (!$input.val() && $input.prop('required')) {
        return false
    } else if (!$input.val() && !$input.prop('required')) {
        return true
    } else if ($input.val()) {
        let regxp = _parseRule($input);
        return regxp.test($input.val()) ? true : false;
    }
};
/**
 * 解析data-rule
 * @param  {Object} $input [jq对象]
 * @return {Object}        [返回一个正则表达式对象(如果是'select|3-10'类型的会返回位数)]
 */
let _parseRule = function($input) {
    let rule = $input.data('rule').trim().toLowerCase();
    let type = rule.match(/^(\w+)(\|[\w|\W]+)?/)[1];
    let pattern;
    //判断type是否存在 》属于哪种rule类型 》对第二种类型进一步分类解析
    if (!patterns.hasOwnProperty(type)) {
        throw type + ' is not registered'
    } else if (/^\w+$/.test(rule)) {
        if (type == 'regexp') {
            pattern = new RegExp($input.prop('pattern'));
        } else {
            pattern = patterns[type];
        }
    } else if (/^\w+\|[\w|\W]+/.test(rule)) {
        // 'size:1-10,decimal:1-2,type:+1'            
        let exp = rule.slice(type.length + 1); //需要扣除掉'|''
        let obj = {};
        exp.split(',').forEach((item) => {
            obj[item.split(':')[0]] = item.split(':')[1]
        })
        if (type == 'number') {
            if (obj['size'] && !obj.hasOwnProperty('decimal') && !obj.hasOwnProperty('type')) {
                // /^-?\d{1,3}$/  限定位数的整数
                pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}$');
            } else if (obj['size'] && obj['type'] == '+1' && !obj.hasOwnProperty('decimal')) {
                // /^\d{1,3}$/  限定位数的正整数
                pattern = new RegExp('^\\d{' + obj['size'].replace('-', ',') + '}$');
            } else if (obj['size'] && obj['type'] == '-1' && !obj.hasOwnProperty('decimal')) {
                // /^-\d{1,3}$/  限定位数的负整数
                pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}$');
            } else if (obj['type'] == '+1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) {
                // /^\d+$/  不限定位数的正整数
                pattern = new RegExp('^\\d+$');
            } else if (obj['type'] == '-1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) {
                // /^-\d+$/  不限定位数的负整数
                pattern = new RegExp('^-\\d+$');
            } else if (obj['decimal'] && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('type')) {
                // /^-?\d+\.\d{1,3}$/  浮点数(正负均可),只定小数点的
                pattern = new RegExp('^-?\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            } else if (!obj.hasOwnProperty('size') && obj['type'] == '+1' && obj['decimal']) {
                // /^\d+\.\d{1,3}$/  正浮点数,只定小数点的
                pattern = new RegExp('^\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            } else if (!obj.hasOwnProperty('size') && obj['type'] == '-1' && obj['decimal']) {
                // /^-\d+\.\d{1,3}$/  负浮点数,只定小数点的
                pattern = new RegExp('^-\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            } else if (obj['size'] && !obj.hasOwnProperty('type') && obj['decimal']) {
                // /^-?\d+\.\d{1,3}$/  浮点数(正负均可),整数部分和小数部分均限定
                pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            } else if (obj['size'] && obj['type'] == '+1' && obj['decimal']) {
                // /^\d{1,3}\.\d{1,3}$/  正浮点数,整数部分和小数部分均限定
                pattern = new RegExp('^\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            } else if (obj['size'] && obj['type'] == '-1' && obj['decimal']) {
                // /^-\d{1,3}\.\d{1,3}$/  负浮点数,整数部分和小数部分均限定
                pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$');
            }
        } else if (type == 'zh') {
            //砍掉尾巴2位('+$''),然后拼接{form,to}       
            pattern = new RegExp('^[\\u4e00-\\u9fa5]{' + obj['size'].replace('-', ',') + '}$');
        } else if (type == 'en') {
            pattern = new RegExp('^[a-zA-Z]{' + obj['size'].replace('-', ',') + '}$')
        } else if (type == 'select') {
            pattern = obj['size'];
        }
    } else {
        throw rule + ' is wrong.';
    }
    return pattern
};
// blur校验input value
$(document).on('change', ':input[data-rule]', function(event) {
    if ($(this).type == 'checkbox' || $(this).type == 'radio') return
    if (_checkIt($(this))) {
        _successHandler($(this));
    } else {
        _warn($(this).data('help') || $(this).prop('placeholder') || '格式错误');
        _errorHandler($(this));
    }
})
export default {
    /**
     * 校验指定范围内的所有表单元素的输入是否有效
     * @param  {String} selector [description]
     * @return {Boolean}         [返回true或false]
     */
    validate(selector) {
            let isValid;
            $(selector).find(':input[data-rule]').each(function() {
                if (_checkIt($(this))) {
                    _successHandler($(this));
                    isValid = true;
                } else {
                    _warn($(this).data('help') || $(this).prop('placeholder') || '格式错误');
                    _errorHandler($(this));
                    isValid = false;
                    return false
                }
            })
            return isValid;
        },
        /**
         * 注册一个新的校验类型
         * @param  {Object} object {type:'REG_NAME',pattern:正则表达式}
         * @return {[type]} 往默认的defaults对象里添加新的键值对
         */
        register(object) {
            $.each(object, function(key, val) {
                if (!/^REG_/i.test(key)) {
                    throw object.type + ' is invalid name.it must start with "REG_"'
                }
                //默认属性放后头,确保不会被新注册的覆盖    
                $.extend(patterns, {
                    [key.toLowerCase()]: val
                }, defaults);
            })
        },
        /**
         * 暴露出来的重置接口
         * @param  {String}     type ['warn|errorHandler|successHandler',warn表示展示错误信息;errorHander表示错误时执行的操作]
         * @param  {Function}   slot [函数或字符串]
         * @return                   [重置内部的方法]
         */
        reset(type, slot) {
            if (type == 'warn') {
                _warn = slot;
            } else if (type == 'errorHandler') {
                _errorHandler = slot;
            } else if (type == 'successHandler') {
                _successHandler = slot;
            } else {
                throw type + ' is not invalid type'
            }
        }
}

github地址

参考

weui.js – form模块

    原文作者:soon
    原文地址: https://segmentfault.com/a/1190000010468889
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞