【MUI晋级】开发MUI自定义表单插件二,插件封装(持续更新)

【MUI晋级】开发MUI自定义表单插件一,基础插件模板搭建
【MUI晋级】开发MUI自定义表单插件二,插件封装

上一篇我们讲了基本的插件模板和mui的样式重置,今天来说一下如何在插件模板的基础上封装成插件!

使用自定义表单有如下好处

  1. 可以后台配置表单内容
  1. 新增页、编辑页可以共用一段代码
  2. 方便表单内容的获取

首先要说明一下我们的自定义表单要实现哪些功能,才能更好去封装

  1. 通过JS生成form表单,表单类型、默认值、样式、提示文字都可以自由定义
  1. 每个表单项都可以绑定多个事件
  2. 要有表单提交事件,可以实时的获取表单的值

一、通过JS生成form表单

我们首先先观察一些DOM结构

DOM结构

可以得出以下结论

  1. 存放表单的容器是必须的,初始化参数一定要有
  1. form节点只有一个,但是class需要设置
  2. 每一项的BOX容器是多个,所以初始化参数一定是一个数组
  3. 标题元素需要设置class和文字
  4. input需要设置的就比较多了,各种属性,而且还不仅仅是 input

根据以上结论,我们先写一个初始化参数,打开 customForm.js ,编写原来写好的defaultConfig 的值

//默认参数
var defaultConfig = {
    storageBox:null,         //存放表单的容器
    
    formNode:{               //表单节点
        id:"",               //表单的ID
        className:"",        //表单的样式,多个样式用空格隔开
        submitFn:null        //表单提交事件
    },
    
    formItems:[{             //表单列表项
        boxClass:"",         //box容器的样式名
        titleText:"",        //标题元素的文字
        titleClass:"",       //标题元素的样式
        input:{              //input的一些配置项
            tagName:"",      //input的标签名,如input、select、textarea
            attr:{           //input的属性配置项,键为属性名,值为属性值
                id:'',       
                name:''      //只要是合法的input属性都可以写到这里,还有很多就不列举了
            },
            event:{          //事件列表,只要是合法的事件名都可以
                tap:function(){
                    
                },
                focus:function(){
                    
                }
            },
            value:"",       //input和textarea用字符串格式
            value:[{        //select的用数组格式
                text:"",    //显示的文字
                value:'',   //值
                isSelected:true //是否选中
            }]
        }
        
        defaultClass:{                          //在未设置样式的情况下使用默认样式
            formNodeClass:"mui-input-group",    //form节点默认样式
            formItemClass:'mui-input-row',      //表单每一项的box容器样式
            inputClass:'mui-input-clear'        //input的样式
        }
    }]
};

我们把 defaultConfig 分为了4大块

  1. storageBox 存放表单的容器,Element类型
  2. formNode 表单节点
  3. formItems 表单列表项
  4. defaultClass 默认样式

但是我们的默认值只能存一个 defaultClass 默认样式,其他的都需要作为初始化参数传进来,所以将我们刚才写的 defaultConfig注释掉,重新写一个并更改为如下

var defaultConfig = {
    defaultClass:{                          //在未设置样式的情况下使用默认样式
        formNodeClass:"mui-input-group",    //form节点默认样式
        formItemClass:"mui-input-row",      //表单每一项的box容器样式
        inputClass:"mui-input-clear"        //input的样式
    }
};

更改init方法

我们需要更改一下插件模板的init方法,来检查下初始化参数的正确性并打印一下合并默认参数之后的值

init:function(arg){
    var self = this;
    
    if(arg) {
        if(arg.storageBox && arg.storageBox.nodeType === 1) {
            //将初始化参数和默认参数合并,并存放到对象的config属性下
            self.config = $.extend(arg, defaultConfig);
            console.log(self.config);
        } else {
            self._logError("初始化参数[storageBox]参数未设置或者不是Element类型", true);
        }
    } else {
        self._logError("初始化参数不存在");
    }
    
    return self;
}

前台调用方法

//初始化参数
var customFormCg = {
    storageBox:mui(".mui-content")[0]
};

//实例化
var customForm = new mui.customForm(customFormCg);

谷歌浏览器下截图,编辑器的控制台只能输出一个 "[object Object]",没法查看详细信息


谷歌调试截图

当做到这一步就说明你的插件初始化已经成功了,接下来我们写一个 _InitHtml 方法来创建form表单,并修改init方法让其调用_InitHtml方法;

/** 实例化调用函数
 * -------------------------
 * @param {Object} arg 插件配置参数
 */
init:function(arg){
    var self = this;
    
    if(arg) {
        if(arg.storageBox && arg.storageBox.nodeType === 1) {
            //将初始化参数和默认参数合并,并存放到对象的config属性下
            self.config = $.extend(arg, defaultConfig);
            self._InitHtml();
        } else {
            self._logError("初始化参数[storageBox]参数未设置或者不是Element类型", true);
        }
    } else {
        self._logError("初始化参数不存在");
    }
    
    return self;
},

/**
 * 内部方法,初始化html
 */
_InitHtml:function(){
    var self = this,            //缓存this
        cg = self.config;       //取到配置信息
    
    alert(1);
    
    return self;
},

如果能弹出个1说明你又成功了~,接下来继续编写 _InitHtml方法

/**
 * 内部方法,初始化html
 */
_InitHtml:function(){
    var self = this,            //缓存this
        cg = self.config;       //取到配置信息
        
        
    /* 1. 创建form节点存到对象配置信息里面的formNode下的node里面,并设置样式 */
    !cg.formNode && (cg.formNode = {});
    var form = cg.formNode['node'] = dom.createElement('form');
    form.className = cg.formNode.className || cg.defaultClass.formNodeClass || "";
    
    /* 2.创建表单项 */
    if(cg.formItems && Array.isArray(cg.formItems)){
        for(var i = 0,l = cg.formItems.length;i<l;i++){
            var item = cg.formItems[i],                                                     //缓存当前循环条目
                itemInput = item.input,                                                     //input配置项
                itemBox = item['itemBox'] = dom.createElement("div"),                       //每一项的box容器
                titleEl = item['titleEl'] = dom.createElement("label"),                     //标题元素
                inputEl = item['inputEl'] = dom.createElement(itemInput.tagName || 'input');//input元素
                
            //设置class
            itemBox.className = item.boxClass || cg.defaultClass.formItemClass || '';
            titleEl.className = item.titleClass || '';
            inputEl.className = cg.defaultClass.inputClass || '';
            
            //设置标题文字信息
            titleEl.innerText = item.titleText || '';
            
            //设置input属性
            for(var key in itemInput.attr){
                inputEl.setAttribute(key,itemInput.attr[key]);
            }
            
            //设置input的value
            if(itemInput.value){
                switch(inputEl.tagName.toLowerCase()){
                    case 'select':
                        if(Array.isArray(itemInput.value)){
                            //注意此处不要用i来当做循环索引了
                            for(var x = 0;x<itemInput.value.length;x++){
                                var option = dom.createElement('option');
                                option.setAttribute("value", itemInput.value[x].value);
                                option.innerText = itemInput.value[x].text;
                                itemInput.value[x].isSelected && option.setAttribute('selected', 'selected');
                                inputEl.appendChild(option);
                            }
                        }else{
                            self._logError("当tagName为select时,input下的value属性必须为数组格式");
                        }
                        break;
                    default:
                        inputEl.value = itemInput.value;
                }
            }
            
            //追加元素
            itemBox.appendChild(titleEl);
            itemBox.appendChild(inputEl);
            form.appendChild(itemBox);
        }
    }
    
    //将form节点追加到存放表单的容器里面
    cg.storageBox.appendChild(form);
    return self;
}

将前台“mui-content”里面的内容注释掉,然后修改前台初始化参数 customFormCg

//初始化参数
var customFormCg = {
    storageBox:mui(".mui-content")[0],
    formNode:{
        id:"mainForm"
    },
    formItems:[{
        titleText:'客户名称',
        input:{
            attr:{
                type:"text",
                id:"clientName",
                name:"clientName",
                placeholder:"请输入客户名称"
            }
        }
    },{
        titleText:'客户地址',
        input:{
            attr:{
                type:"text",
                id:"clientAddress",
                name:"clientAddress",
                readonly:"readonly",
                placeholder:"点击选择客户地址"
            }
        }
    },{
        titleText:'客户电话',
        input:{
            attr:{
                type:"number",
                id:"clientTel",
                name:"clientTel",
                placeholder:"请输入客户联系电话"
            },
            value:"18866655444"
        }
    },{
        titleText:'客户级别',
        input:{
            tagName:'select',
            attr:{
                type:"number",
                id:"clientTel",
                name:"clientTel",
                placeholder:"请输入客户联系电话"
            },
            value:[{
                text:"A级",
                value:1
            },{
                text:"B级",
                value:2
            },{
                text:"C级",
                value:3,
                isSelected:true
            }]
        }
    },{
        titleText:'备注',
        input:{
            tagName:"textarea",
            attr:{
                id:"clientTel",
                name:"clientTel",
                placeholder:"请输入备注信息"
            },
        }
    }]
};

如果你得得到如下截图就说明你成功了~

成功截图

接下来我们写一个 getValues 的外部方法用来获取表单参数,我们接受一个input的id组成的数组,可以指定返回结果,如果不传则返回表单全部内容;

/** 获取表单内容
 * @param {array} ids 指定的input ID数组
 * @return {JSON} id为键,value值为值
 */
getValues:function(ids){
    var self = this,
        cg = self.config;
        
    var data = {},
        isAge = ids && Array.isArray(ids);
        
    for(var i = 0; i < cg.formItems.length; i++) {
        var item = cg.formItems[i],
            inputEl = item['inputEl'],
            inputElId = inputEl.getAttribute("id");
        
        if(isAge && ids.indexOf(inputElId) == -1) {
            continue;
        } else {
            data[inputElId] = inputEl.value;
        }
    }
    
    return data;
},

为前台的header里面的保存按钮增加一个id “saveBtn

header

在index.html里面的JS部分为其绑定事件

document.getElementById("saveBtn").addEventListener("tap",function(){
    console.log(JSON.stringify(customForm.getValues()));
});
截图

在浏览器里面点下保存看能否正常打印出信息

谷歌浏览器截图

附带参数测试
console.log(JSON.stringify(customForm.getValues(['clientName','clientTel','clientLv'])));

到此初始化HTML就完结了,接下来该绑定事件了

二、绑定事件

绑定的事件主要有两个一个是form提交事件,另一个是input的事件,我们写一个内部方法 _EventInit

_EventInit:function(){
    var self = this,
        cg = self.config;
    
    //表单提交事件
    cg.formNode['node'].addEventListener("submit",function(e){
        e.preventDefault();     //阻止提交
        cg.formNode.submitFn && cg.formNode.submitFn(self);
    });

    //为input绑定事件
    for(var i = 0;i<cg.formItems.length;i++){
        var item = cg.formItems[i];
        for(var key in item.input.event){
            bindEvent(item.inputEl,key,item.input.event[key]);
        }
    }
    
    /** 内部调用函数
     *  --------------
     * @param {Object} eNode 事件节点
     * @param {Object} eName 事件名
     * @param {Object} eFn 事件函数
     */
    function bindEvent(eNode,eName,eFn){
        eNode.addEventListener(eName,eFn);
    }
    
    return self;
}

更改 init 方法,这就是我们之前说的链式调用
self._InitHtml()._EventInit();

前台为客户地址添加一个 tap 事件,看能否正常弹出一个1

{
    titleText:'客户地址',
    input:{
        attr:{
            type:"text",
            id:"clientAddress",
            name:"clientAddress",
            readonly:"readonly",
            placeholder:"点击选择客户地址"
        },
        event:{
            tap:function(){
                alert(1);
            }
        }
    }
}

如果成功了,其他的事件添加方法类似,比如focus、blur、change等事件。接下来我们来测试一下表单提交事件!

将初始化参数 formNode 修改为如下

formNode:{
    id:"mainForm",
    submitFn:function(){
        alert("我触发了表单提交事件!");
    }
}

然后将 saveBtn 绑定事件修改一下

document.getElementById("saveBtn").addEventListener("tap",function(){
    customForm.config.formNode.node.submit();
});

然后你会发现.......尼玛竟然没反应,这是正常的,因为submit()并不会触发onsubmit(在绑定事件的时候on都是去掉的,所以我们绑定的是submit事件),我们换一个思路,我们直接触发表单的submit事件不就可以了吗,正好mui有这个方法 MUI事件触发,我们修改一下代码

document.getElementById("saveBtn").addEventListener("tap",function(){
    mui.trigger(customForm.config.formNode.node,'submit');
});

这次可以这场的弹出信息,但是这样的写法太麻烦了,所以我们要为 customForm 在扩展一个 onSubmit 的方法

/** 
 * 主动触发表单提交
 */
onSubmit: function(){
    var self = this;
    mui.trigger(self.config.formNode.node,'submit');
    return self;
},

然后修改事件绑定

document.getElementById("saveBtn").addEventListener("tap",function(){
    customForm.onSubmit();
});

到此我们的自定义表单算是完成了,当然还有其他的一些地方要做,比如这样的情况

还有这样的

还有最常用的表单验证,这些这就需要你来修改一下代码,动动小脑筋了。还有如何适应多种表单情况都需要自己不断的尝试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容