【MUI晉級】開發(fā)MUI自定義表單插件二物遇,插件封裝(持續(xù)更新)

【MUI晉級】開發(fā)MUI自定義表單插件一,基礎插件模板搭建
【MUI晉級】開發(fā)MUI自定義表單插件二娘赴,插件封裝

上一篇我們講了基本的插件模板和mui的樣式重置规哲,今天來說一下如何在插件模板的基礎上封裝成插件!

使用自定義表單有如下好處

  1. 可以后臺配置表單內(nèi)容
  1. 新增頁诽表、編輯頁可以共用一段代碼
  2. 方便表單內(nèi)容的獲取

首先要說明一下我們的自定義表單要實現(xiàn)哪些功能唉锌,才能更好去封裝

  1. 通過JS生成form表單,表單類型竿奏、默認值袄简、樣式、提示文字都可以自由定義
  1. 每個表單項都可以綁定多個事件
  2. 要有表單提交事件泛啸,可以實時的獲取表單的值

一绿语、通過JS生成form表單

我們首先先觀察一些DOM結(jié)構(gòu)

DOM結(jié)構(gòu)

可以得出以下結(jié)論

  1. 存放表單的容器是必須的,初始化參數(shù)一定要有
  1. form節(jié)點只有一個候址,但是class需要設置
  2. 每一項的BOX容器是多個吕粹,所以初始化參數(shù)一定是一個數(shù)組
  3. 標題元素需要設置class和文字
  4. input需要設置的就比較多了,各種屬性岗仑,而且還不僅僅是 input

根據(jù)以上結(jié)論匹耕,我們先寫一個初始化參數(shù),打開 customForm.js 赔蒲,編寫原來寫好的defaultConfig 的值

//默認參數(shù)
var defaultConfig = {
    storageBox:null,         //存放表單的容器
    
    formNode:{               //表單節(jié)點
        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的用數(shù)組格式
                text:"",    //顯示的文字
                value:'',   //值
                isSelected:true //是否選中
            }]
        }
        
        defaultClass:{                          //在未設置樣式的情況下使用默認樣式
            formNodeClass:"mui-input-group",    //form節(jié)點默認樣式
            formItemClass:'mui-input-row',      //表單每一項的box容器樣式
            inputClass:'mui-input-clear'        //input的樣式
        }
    }]
};

我們把 defaultConfig 分為了4大塊

  1. storageBox 存放表單的容器,Element類型
  2. formNode 表單節(jié)點
  3. formItems 表單列表項
  4. defaultClass 默認樣式

但是我們的默認值只能存一個 defaultClass 默認樣式浑槽,其他的都需要作為初始化參數(shù)傳進來蒋失,所以將我們剛才寫的 defaultConfig注釋掉,重新寫一個并更改為如下

var defaultConfig = {
    defaultClass:{                          //在未設置樣式的情況下使用默認樣式
        formNodeClass:"mui-input-group",    //form節(jié)點默認樣式
        formItemClass:"mui-input-row",      //表單每一項的box容器樣式
        inputClass:"mui-input-clear"        //input的樣式
    }
};

更改init方法

我們需要更改一下插件模板的init方法桐玻,來檢查下初始化參數(shù)的正確性并打印一下合并默認參數(shù)之后的值

init:function(arg){
    var self = this;
    
    if(arg) {
        if(arg.storageBox && arg.storageBox.nodeType === 1) {
            //將初始化參數(shù)和默認參數(shù)合并篙挽,并存放到對象的config屬性下
            self.config = $.extend(arg, defaultConfig);
            console.log(self.config);
        } else {
            self._logError("初始化參數(shù)[storageBox]參數(shù)未設置或者不是Element類型", true);
        }
    } else {
        self._logError("初始化參數(shù)不存在");
    }
    
    return self;
}

前臺調(diào)用方法

//初始化參數(shù)
var customFormCg = {
    storageBox:mui(".mui-content")[0]
};

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

谷歌瀏覽器下截圖,編輯器的控制臺只能輸出一個 "[object Object]"镊靴,沒法查看詳細信息


谷歌調(diào)試截圖

當做到這一步就說明你的插件初始化已經(jīng)成功了铣卡,接下來我們寫一個 _InitHtml 方法來創(chuàng)建form表單,并修改init方法讓其調(diào)用_InitHtml方法偏竟;

/** 實例化調(diào)用函數(shù)
 * -------------------------
 * @param {Object} arg 插件配置參數(shù)
 */
init:function(arg){
    var self = this;
    
    if(arg) {
        if(arg.storageBox && arg.storageBox.nodeType === 1) {
            //將初始化參數(shù)和默認參數(shù)合并煮落,并存放到對象的config屬性下
            self.config = $.extend(arg, defaultConfig);
            self._InitHtml();
        } else {
            self._logError("初始化參數(shù)[storageBox]參數(shù)未設置或者不是Element類型", true);
        }
    } else {
        self._logError("初始化參數(shù)不存在");
    }
    
    return self;
},

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

如果能彈出個1說明你又成功了~踊谋,接下來繼續(xù)編寫 _InitHtml方法

/**
 * 內(nèi)部方法蝉仇,初始化html
 */
_InitHtml:function(){
    var self = this,            //緩存this
        cg = self.config;       //取到配置信息
        
        
    /* 1. 創(chuàng)建form節(jié)點存到對象配置信息里面的formNode下的node里面,并設置樣式 */
    !cg.formNode && (cg.formNode = {});
    var form = cg.formNode['node'] = dom.createElement('form');
    form.className = cg.formNode.className || cg.defaultClass.formNodeClass || "";
    
    /* 2.創(chuàng)建表單項 */
    if(cg.formItems && Array.isArray(cg.formItems)){
        for(var i = 0,l = cg.formItems.length;i<l;i++){
            var item = cg.formItems[i],                                                     //緩存當前循環(huán)條目
                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來當做循環(huán)索引了
                            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屬性必須為數(shù)組格式");
                        }
                        break;
                    default:
                        inputEl.value = itemInput.value;
                }
            }
            
            //追加元素
            itemBox.appendChild(titleEl);
            itemBox.appendChild(inputEl);
            form.appendChild(itemBox);
        }
    }
    
    //將form節(jié)點追加到存放表單的容器里面
    cg.storageBox.appendChild(form);
    return self;
}

將前臺“mui-content”里面的內(nèi)容注釋掉轿衔,然后修改前臺初始化參數(shù) customFormCg

//初始化參數(shù)
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:"請輸入客戶聯(lián)系電話"
            },
            value:"18866655444"
        }
    },{
        titleText:'客戶級別',
        input:{
            tagName:'select',
            attr:{
                type:"number",
                id:"clientTel",
                name:"clientTel",
                placeholder:"請輸入客戶聯(lián)系電話"
            },
            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 的外部方法用來獲取表單參數(shù),我們接受一個input的id組成的數(shù)組嫌褪,可以指定返回結(jié)果呀枢,如果不傳則返回表單全部內(nèi)容;

/** 獲取表單內(nèi)容
 * @param {array} ids 指定的input ID數(shù)組
 * @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()));
});
截圖

在瀏覽器里面點下保存看能否正常打印出信息

谷歌瀏覽器截圖

附帶參數(shù)測試
console.log(JSON.stringify(customForm.getValues(['clientName','clientTel','clientLv'])));

到此初始化HTML就完結(jié)了笼痛,接下來該綁定事件了

二裙秋、綁定事件

綁定的事件主要有兩個一個是form提交事件,另一個是input的事件缨伊,我們寫一個內(nèi)部方法 _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]);
        }
    }
    
    /** 內(nèi)部調(diào)用函數(shù)
     *  --------------
     * @param {Object} eNode 事件節(jié)點
     * @param {Object} eName 事件名
     * @param {Object} eFn 事件函數(shù)
     */
    function bindEvent(eNode,eName,eFn){
        eNode.addEventListener(eName,eFn);
    }
    
    return self;
}

更改 init 方法,這就是我們之前說的鏈式調(diào)用
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等事件。接下來我們來測試一下表單提交事件谭胚!

將初始化參數(shù) formNode 修改為如下

formNode:{
    id:"mainForm",
    submitFn:function(){
        alert("我觸發(fā)了表單提交事件徐块!");
    }
}

然后將 saveBtn 綁定事件修改一下

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

然后你會發(fā)現(xiàn).......尼瑪竟然沒反應未玻,這是正常的,因為submit()并不會觸發(fā)onsubmit(在綁定事件的時候on都是去掉的胡控,所以我們綁定的是submit事件)扳剿,我們換一個思路,我們直接觸發(fā)表單的submit事件不就可以了嗎昼激,正好mui有這個方法 MUI事件觸發(fā)庇绽,我們修改一下代碼

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

這次可以這場的彈出信息,但是這樣的寫法太麻煩了橙困,所以我們要為 customForm 在擴展一個 onSubmit 的方法

/** 
 * 主動觸發(fā)表單提交
 */
onSubmit: function(){
    var self = this;
    mui.trigger(self.config.formNode.node,'submit');
    return self;
},

然后修改事件綁定

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

到此我們的自定義表單算是完成了瞧掺,當然還有其他的一些地方要做,比如這樣的情況

還有這樣的

還有最常用的表單驗證凡傅,這些這就需要你來修改一下代碼辟狈,動動小腦筋了。還有如何適應多種表單情況都需要自己不斷的嘗試

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末夏跷,一起剝皮案震驚了整個濱河市上陕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拓春,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亚隅,死亡現(xiàn)場離奇詭異硼莽,居然都是意外死亡,警方通過查閱死者的電腦和手機煮纵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門懂鸵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人行疏,你說我怎么就攤上這事匆光。” “怎么了酿联?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵终息,是天一觀的道長。 經(jīng)常有香客問我贞让,道長周崭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任喳张,我火速辦了婚禮续镇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘销部。我一直安慰自己摸航,他們只是感情好制跟,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酱虎,像睡著了一般雨膨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逢净,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天哥放,我揣著相機與錄音,去河邊找鬼爹土。 笑死甥雕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的胀茵。 我是一名探鬼主播社露,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼琼娘!你這毒婦竟也來了峭弟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤脱拼,失蹤者是張志新(化名)和其女友劉穎瞒瘸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熄浓,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡情臭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赌蔑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俯在。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖娃惯,靈堂內(nèi)的尸體忽然破棺而出跷乐,到底是詐尸還是另有隱情,我是刑警寧澤趾浅,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布愕提,位于F島的核電站,受9級特大地震影響潮孽,放射性物質(zhì)發(fā)生泄漏揪荣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一往史、第九天 我趴在偏房一處隱蔽的房頂上張望仗颈。 院中可真熱鬧,春花似錦、人聲如沸挨决。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脖祈。三九已至肆捕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盖高,已是汗流浹背慎陵。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喻奥,地道東北人席纽。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像撞蚕,于是被迫代替她去往敵國和親润梯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容