项目总结 关于layui使用上的坑

  • 感悟源头

最近kk在作一个小型的功能业务平台,但因为客户对浏览器兼容性的要求比较强(兼容IE低版本),从技术选型上也须要配合后台研发进行。因此最后采用了一套偏后端人员开发的方案:javascript

  • 功能组件:layui
    • 模板引擎:laytpl
    • UI组件:layui.element、layui.laydate(日历)
  • 响应式布局:Boostrap
    • 表单验证:Boostrap Validator
  • 后端开发:GUNS(一个利于快速搭建业务平台的框架)

与此同时,还包括有脱离项目页面的:html

  • EDM邮件信
  • 下载报告:html形式存储本地浏览

虽然说这些技术栈确实是kk初学前端时就接触过的,但layui着实在开发过程当中坑了kk一把,因此在这里想作个总结。前端

不过这个项目也不是那么没突破,借此机会,kk确实仍是接触了一把邮件信的编写,属于HTML基础上的一次突破,也算是一次很好的经验借鉴。java

事不宜迟,我们开始。ajax

  • 开发工具类方法

  • 1. Boostrap Validator的方法封装

var Feng = {
        initValidator: function (formId, fields) {
        $('#' + formId).bootstrapValidator({
            feedbackIcons: {
                valid: 'glyphicon glyphicon-ok',
                invalid: 'glyphicon glyphicon-remove',
                validating: 'glyphicon glyphicon-refresh'
            },
            fields: fields,
            live: 'enabled',
            message: '该字段不能为空'
            });
        }
    };
复制代码

该方法封装在了工具类Feng里,可供业务页面全局调用。json

//设置validator校验规则
var validateFields = {
    time: {
        trigger:"change input click", //监听change动做
        validators: {
            notEmpty: {
                message: '执行时间不能为空'
            }
        }
    },
    caption: {
        validators: {
            notEmpty: {
                message: '任务名称不能为空'
            }
        }
    }
};
//业务页面内初始化就调用Feng工具类
$(function () {
    Feng.initValidator("taskInfoForm", validateFields);
});

//点击提交触发表单校验,验证数据是否为空
UserInfoDlg.validate = function () {
    $('#userInfoForm').data('bootstrapValidator').validate();//手动对表单进行校检
    $('#taskInfoForm').data('bootstrapValidator').validate();//手动对表单进行校检
    return $('#userInfoForm').data('bootstrapValidator').isValid() && $('#taskInfoForm').data('bootstrapValidator').isValid();
};
复制代码
  • 2. 基于JQ手动封装ajax方法

(function () {
    var $ax = function (url, success, error) {
        this.url = url;
        this.type = "post";
        this.data = {};
        this.dataType = "json";
        this.async = false;
        this.success = success;
        this.error = error;
    };

    $ax.prototype = {
        start: function () {
            var _this = this;

            if (this.url.indexOf("?") == -1) {
                this.url = this.url + "?jstime=" + new Date().getTime();
            } else {
                this.url = this.url + "&jstime=" + new Date().getTime();
            }
            jQuery.ajax({
                url: this.url,
                type: this.type,
                dataType: this.dataType,
                async: this.async,
                data: this.data,
                success: function (e) {
                    _this.success(e);
                },
                error: function (data) {
                    _this.error(data);
                }
            });
        },

        set: function (key, value) {
            if (typeof key == "object") {
                for (var i in key) {
                    if (typeof i == "function")
                        continue;
                    this.data[i] = key[i];
                }
            } else {
                this.data[key] = (typeof value == "undefined") ? $("#" + key).val() : value;
            }
            return this;
        },

        setData: function (data) {
            this.data = data;
            return this;
        },

        clear: function () {
            this.data = {};
            return this;
        }
    };

    window.$ax = $ax;

}());
复制代码

业务页面调用$ax:bootstrap

//提交信息
    var ajax = new $ax(Feng.ctxPath + "/task/add", function (data) {//传入ajax成功(success)后的callback方法
        Feng.success("添加成功!");
        if (window.parent.MgrUser != undefined) {
            window.parent.MgrUser.table.refresh();
            UserInfoDlg.close();
        }
        window.location.reload();
    }, function (data) {//传入ajax失败(error)后的callback方法
        Feng.error("添加失败!" + data.responseJSON.message + "!");
    });
    ajax.set(this.userInfoData);//设置提交的表单信息,$ax对象根据输入data的类型放进this.data中
    ajax.start();//发出ajax请求
复制代码
  • 3. 表单取值

该方法根据输入的表单名name,或是class,判断该表单输入哪一种类型(radio、checkbox、日历、普通输入框),进行不一样方式的取值和轻量校验。后端

/** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */
UserInfoDlg.set = function (key, value) {
    if (typeof value == "undefined") {
        if (key == 'time') {
            var value = $("#" + key).val();
            var startTime = value.substring(0, value.indexOf("-")).replace(/(\w*)年(.*)月(.*)日(.*)/g, "$1-$2-$3").replace(/\s*/g, "");
            var endTime = value.substring(value.indexOf("-") + 1).replace(/(\w*)年(.*)月(.*)日(.*)/g, "$1-$2-$3").replace(/\s*/g, "");
            console.log(startTime);
            console.log(endTime);
            this.userInfoData['startTime'] = startTime;
            this.userInfoData['endTime'] = endTime;
            return this;
        }
        if (typeof $("#" + key).val() == "undefined") {
            var str = "";
            var ids = "";
            $("input[name='" + key + "']:checkbox").each(function () {
                if (true == $(this).is(':checked')) {
                    str += $(this).val() + ",";
                }
            });
            if (str && key != 'time') {
                if (str.substr(str.length - 1) == ',') {
                    ids = str.substr(0, str.length - 1);
                }
            } else {
                $("input[name='" + key + "']:radio").each(function () {
                    if (true == $(this).is(':checked')) {
                        ids = $(this).val()
                    }
                });
            }
            this.userInfoData[key] = ids;
        } else {
            this.userInfoData[key] = $("#" + key).val();
        }
    }

    return this;
};
复制代码

业务页面调用: 使用JQ链式调用,将表单全部项目的name推入便可浏览器

/** * 收集数据 */
UserInfoDlg.collectData = function () {
    this.set('orgName').set('orgCode').set('orgContactPerson').set('orgContactMobile').set('caption')
        .set('url').set('time').set('period').set('importance').set('smsList').set('emailList').set('depth');
};
复制代码
  • 坑1:日历组件和表单验证

  • 1. 业务场景

如上图为一个添加了Boostrap Validator表单验证的双日历(layui.laydate)输入框,表单提交前须要对其输入的内容进行非空校验。
框架

  • 2. 技术实现

其结构组成代码以下:

<!-- 传入jQuery文件-->
    
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    
    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->

    <!--引入BoostrapValidator-->
    
    <!--引入laydate -->
  
    <div class="form-horizontal">
        <div class="row form-row">
            <div class="col-sm-12 form-group">
                <div class="form-group" style="">
                    <label class="col-sm-2 control-label">
                        <span style="color:red;">*</span>执行时间:
                    </label>
                    <div class="col-sm-5">
                        <input class="form-control" placeholder="请选择开始日期 ~ 结束日期" id="time" name="time" value="" type="text">
                    </div>
                </div>
            </div>
        </div>
    </div>
复制代码

laydate组件配置代码:

laydate.render({
        elem: '#time'
        , range: true //或 range: '~' 来自定义分割字符
        , format: 'yyyy年MM月dd日' //可任意组合
        , done: function (value, date) {
            $("#time").change();
            setTimeout(function () {
              $('#taskInfoForm')
              .data('bootstrapValidator')
                    .updateStatus('time','NOT_VALIDATED', null)
                    .validateField('time');
            }, 200)
        }
    });
    //直接监听日历输入框的blur事件
    $("#time").blur(function () {
        setTimeout(function () {
            $('#taskInfoForm').data('bootstrapValidator')
                .updateStatus('time', 'NOT_VALIDATED', null)
                .validateField('time');
        }, 200)
    });
复制代码
  • 3. 掉进的坑

Boostrap Validator对第三方js输入表单的信息,没法自动触发校验。

实如今业务场景中,就是当laydate双日历选定日期事后,点击提交触发校验,仍然视为输入框为空。

  • 4.解决方案

第一步,在日历输入框所对应的校验规则validatorField里添加:

trigger:"change input click", 
复制代码

即input的这些事件皆可自动触发validate()。

done: function (value, date) {
            $("#time").change();
              $('#taskInfoForm')
              .data('bootstrapValidator')
                    .updateStatus('time','NOT_VALIDATED', null)
                    .validateField('time');
        }
复制代码

第二步:在laydate完成done事件后触发input的change事件并手动触发校验一次。

结果:能够实现日历值变化时候的表单从新校验。

这下新问题出现了:

在输入框从空值到有值的过程当中,仍然没法触发校验

  • 5.根本缘由

kk探究了下各路大神的文章,发现其问题根本在于:

laydate 加载日期赋值给 input 在 bootstrapValidator 验证以后,因此在点击时间插件以后进行二次特定字段验证便可 取自文章:bootstrapValidator 验证框架与 layui 时间插件兼容

  • 6.优化解决方案

1.完善laydate的done回调事件,对validate二次校验设置时间延迟(200ms基本不影响体验)

done: function (value, date) {
            $("#time").change();
            setTimeout(function () {
              $('#taskInfoForm')
              .data('bootstrapValidator')
                    .updateStatus('time','NOT_VALIDATED', null)
                    .validateField('time');
            }, 200)
        }
复制代码

2.附加的,也能够完善日历输入框自己的blur事件,将validate绑定在blur事件上

$("#time").blur(function () {
        setTimeout(function () {
            $('#taskInfoForm').data('bootstrapValidator')
                .updateStatus('time', 'NOT_VALIDATED', null)
                .validateField('time');
        }, 200)
    });
复制代码

结果:可完整解决当前问题,输入日历可正常触发二次校验。

  • 坑2:模板引擎与UI组件

  • 1.掉进的坑

    laytpl与layui.element不可共用

  • 2.业务场景

    kk想尝试在页面中引用laytpl的面包屑功能,须要导入element的功能组件。

但kk发现其与写在laytpl里的DOM结构代码没法兼容,即laytpl渲染出的结构没法正常使用layui自带的UI组件,或者说已经加载了其DOM结构及样式,但未从layui里初始化其功能逻辑。

  • 3.解决方案

    最终采用了手动重构功能逻辑的方法。

如上内容属于kk我的意见,若有更好的解决方案,或内容有错误遗漏,欢迎指正!

相关文章
相关标签/搜索