【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閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容