源代碼地址:https://github.com/upcyoung/dtEditor
在Web項(xiàng)目中皮官,用于呈現(xiàn)數(shù)據(jù)時(shí)经柴,一般都會(huì)選擇Table。完全自己手寫Table是一項(xiàng)特別繁雜的事情,所以一般會(huì)選擇Table插件燎含。在項(xiàng)目中提鸟,如果需要使用Table時(shí)军援,我一般會(huì)首選jquery datatables,datatables支持豐富的配置称勋、API及擴(kuò)展(Buttion, Selected, Fixedcolumn等)胸哥。
在有些項(xiàng)目中會(huì)要求數(shù)據(jù)可新增、編輯赡鲜,在DataTables的官網(wǎng)中可以找到Editor擴(kuò)展空厌,該擴(kuò)展提供了對(duì)數(shù)據(jù)的編輯功能,但是此擴(kuò)展是收費(fèi)的银酬。由于我已經(jīng)習(xí)慣了使用DataTables嘲更,并不想換到其他的Table插件,所以決定自己寫一個(gè)支持編輯的插件揩瞪。
在編寫插件前赋朦,首先要確定一個(gè)實(shí)現(xiàn)插件的大體思路。在表單錄入中,由哪種方式(輸入宠哄、下拉壹将、日期等)進(jìn)行編輯及驗(yàn)證是多樣化的,如果考慮把這部分也在插件中就太復(fù)雜了(如果有興趣可查看x-editable)毛嫉。所以瞭恰,我希望這個(gè)插件要功能單一,僅提供一個(gè)基架狱庇,編輯的行為在外部實(shí)現(xiàn)惊畏,同時(shí)支持行內(nèi)和彈出兩種編輯方式。
在實(shí)現(xiàn)插件我想到的是在原有的row上為每個(gè)可編輯列生成相應(yīng)的控件密任,還是clone當(dāng)前row并添加到tbody中再為每個(gè)可編輯列生成控件颜启。這里,我選擇了后種方式浪讳。接下要解決的問題是如何為可編輯列生成控件并在結(jié)束時(shí)銷毀各個(gè)控件缰盏。這里借鑒DataTalbe中columnDefs的定義方式,用target來聲名列的位置淹遵,使用render方式返回控件口猜;考慮到控件的數(shù)據(jù)可能是異步加載,增加renderAfter方法透揣;考慮到控件的銷毀和事件的移除济炎,增加destroy方法;為值的獲取增加get方法辐真。這樣編輯列的配置如下须尚。
//可編輯列的配置
window.UpcEditorSetting = function (options) {
var self = this;
self.target = options.target;
self.field = options.field;
self.title = options.title;
if (options.noField) {
self.noField = true;
}
self.$dom = null;
self.handlers = [];
self.render = function () { return ''; };
if (!self.noField) {
self.get = function () {
return $.trim(this.$dom.val());
};
}
if (options.render) {
this.render = options.render;
}
if (options.get) {
this.get = options.get;
}
if (options.destroy) {
this.destroy = options.destroy;
}
if (options.renderAfter) {
this.renderAfter = options.renderAfter;
}
return this;
};
其中,noField用于標(biāo)識(shí)此列為非編輯列侍咱,$dom用于保存想要在上下文中要使用的對(duì)象耐床,handlers保存綁定的事件,用于在銷毀時(shí)移除事件綁定楔脯,field和title用于彈出編輯的支持撩轰。如果不需要上下文this時(shí),可僅聲名具有以上屬性的對(duì)象即可昧廷。
在定好編輯列配置后堪嫂,就可以開始搭主要的架子了。將插件命名為dtEditor麸粮,插件主要實(shí)現(xiàn)編輯溉苛、保存镜廉、取消弄诲、新增、獲取編輯數(shù)據(jù)功能。jquery插件的實(shí)現(xiàn)方式就是要在jquery原型上聲名function就可以了齐遵,如下方式:
//jquery插件的聲名方式
$.prototype.dtEditor=function(options){}
//or
$.fn.dtEditor=function(options){}
//or
$.fn.extend({
dtEditor:function(options){}
})
以下貼出inline編輯模式下可編輯列的初始化和獲取值相關(guān)的代碼寂玲。
var tools = {
getRowData:function() {
if (this.oTrIndex >= 0) {
var dt = this.oDataTable;
var rd = dt.row(this.oTrIndex).data();
var node = dt.row(this.oTrIndex).node();
var tr = $(node).next()[0];
var aoColumns = dt.settings()[0].aoColumns;
var columns = this.columns;
for (var i = columns.length - 1; i >= 0; i--) {
if (columns[i].get) {
var td = $(tr).find("td")[[columns[i].target]];
var d = columns[i].get($(td), $(tr));
rd[aoColumns[columns[i].target].data] = d;
}
}
return rd;
}
return null;
},
inlineAccessor: function (index, tr, row, cache) {
var dt = cache.oDataTable;
var rowClone = $(row.node()).clone(); //clone當(dāng)前行的node
var columns = cache.columns;
$(row.node()).hide();
$(row.node()).after(rowClone);
var tds = rowClone.find("td");
var rd = row.data();
//對(duì)每一列,激活編輯模式
for (var i = cache.columns.length - 1; i >= 0; i--) {
var coordinate = { row: index, column: columns[i].target };
var cell = dt.cell(coordinate); //取得cell
var td = tds[columns[i].target];
var cd = null;
if (!columns[i].noField) {
cd = cell.data();
}
var html = columns[i].render(cd, rd, $(tr)); //調(diào)用render方法梗摇,獲取填充td的html
if (typeof html == "string") {
td.innerHTML = html; //填充td拓哟,編輯狀態(tài)
} else {
$(td).empty().append(html); //jquery對(duì)象,直接append
}
if (columns[i].renderAfter) { //html渲染完之后伶授,執(zhí)行回調(diào)
columns[i].renderAfter(cd, rd, $(tr));
}
}
}
};
插件使用方式如下断序。
var option = {
pop: $('#pop-form'), //彈出編輯列的父容器
columns: [
new UpcEditorSetting({
target: 0,
noField: true
}),
new UpcEditorSetting({
target: 2,
field: 'text',
title: '簡短描述',
render: function(cd) {
this.$dom = $('<input type="text" class="form-control"/>').val(cd);
return this.$dom;
},
renderAfter: function() {
this.$dom.focus();
}
}),
new UpcEditorSetting({
target: 3,
field: 'amount',
title: '數(shù)量',
render: function(cd) {
this.$dom = $('<input type="text" class="form-control"/>').val(cd);
return this.$dom;
}
}),
new UpcEditorSetting({
target: 4,
field: 'meins',
title: '計(jì)量單位',
destroy: function() {
this.$dom.chosen('destroy');
},
get: selectGetter,
render: function(cd, full) {
this.$dom = $('<select type="text" class="form-control"></select>').val(cd);
return this.$dom;
},
renderAfter: function(cd) {
var self = this;
//使用jquery chosen
self.$dom.chosen(chosenOp);
//異步加載數(shù)據(jù)
setTimeout(() => {
var options = [],data=[{"name":"百分比","code":"%"},{"name":"每千","code":"%O"}];
for (var i = 0, l = data.length; i < l; i++) {
options.push('<option data-t="' + data[i].name + '" value="' + data[i].code + '">' + data[i].code + '-' + data[i].name + '</option>');
}
self.$dom.empty().append(options.join('')).val();
if (v) {
self.$dom.val(v);
}
self.$dom.trigger("chosen:updated");
}, 0);
}
}),
new UpcEditorSetting({
target: 5,
field: 'date',
title: '交貨日期',
render: function(cd) {
this.$dom = $('<input type="text" class="form-control"/>');
if (cd) {
this.$dom.val(cd);
} else {
this.$dom.val((new Date()).format());
}
var options = {
autoclose: true,
language: 'zh-CN',
minView: 2,
pickerPosition: 'bottom-left',
format: "yyyy-mm-dd"
};
if (editMode == 0) {
options.pickerPosition = 'top-right';
}
//使用bootstrap datetimepicker
this.$dom.datetimepicker(options);
return this.$dom;
},
destroy: function($td) {
this.$dom.datetimepicker('remove');
}
}),
new UpcEditorSetting({ target: 11, noField: true })
],
defaultValue: { text: '', amount: 1, meins: '',date:'' },
message: messageService.warning
};
$('table').dtEditor(option); //初始化dtEditor
$('table').dtEditor().triggerAdd(); //新增
$('table').dtEditor().triggerEdit(row); //編輯
$('table').dtEditor().triggerCancel(); //取消
$('table').dtEditor().getRowData(); //獲取行數(shù)據(jù)
$('table').dtEditor().triggerSave(); //保存數(shù)據(jù)
效果圖如下。