内嵌编辑
更新:如果您正在为 DataTables 寻找一个完整的编辑解决方案,Editor 现已推出,可以在几分钟内为您的表格添加编辑功能。
此博客文章是在 DataTables 1.10 发布之前编写的,并未使用 1.10 中的新 API 和功能。保留此篇文章供参考,但请参阅手册以获取有关如何使用 DataTables 的说明。
在现代的 Web 应用程序中,表格的一个常见用途是显示一组可以由最终用户修改的信息 - 一个典型的CRUD(创建、读取、更新、删除)场景。其中一个示例是 Web 应用程序的用户列表,管理员可以在表格视图中实时更新用户详细信息。
有很多方法可以显示此表格的编辑界面,您希望使用哪种方法取决于您希望它如何与您的网站/应用程序的其余部分进行交互。您可能希望有一个浮动在表格顶部的编辑灯箱,或者您可以使用类似jEditable的库一次使用一个单元格。通常,我避开为 DataTables 提供编辑插件,因为应该如何实施它会根据不同的情况而有很大不同,而且一刀切在这里并不适用。
幸运的是,使用DataTables API可以轻松创建适合您特定设置的 CRUD 界面。在此示例中,我将展示如何创建提供整行内联编辑的界面。在本教程结束时我们将实现的效果如下图所示
表格
与往常一样,我们需要一个表格作为起点。为此,我将我要制作的可编辑表格放在我的Conditional-CSS 的浏览器支持标准表格的基础之上,再添加两列,一列用于编辑按钮,一列用于删除按钮。当然,您可以根据需要对这些内容进行样式设置或自定义(例如图像)。我还在表格上方添加了一个用于创建新行的链接。标记如下所示
<p><a id="new" href="">Add new row</a></p>
<table cellpadding="0" cellspacing="0" border="0" class="display" id="example">
<thead>
<tr>
<th>Rendering engine</th>
<th>Browser</th>
<th>Platform(s)</th>
<th>Engine version</th>
<th>CSS grade</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr class="odd gradeX">
<td>Trident</td>
<td>Internet Explorer 4.0</td>
<td>Win 95+</td>
<td class="center">4</td>
<td class="center">X</td>
<td><a class="edit" href="">Edit</a></td>
<td><a class="delete" href="">Delete</a></td>
</tr>
...
</tbody>
</table>
DataTables 的初始化很简单
$(document).ready(function() {
var oTable = $('#example').dataTable();
} );
编辑模式
在三个编辑功能(创建、编辑和删除)中,编辑功能是我们这里将首先要解决的问题。我的做法是简单地用一个input标签替换要使它可编辑的行中每个单元格的内容,及将它的值设为单元格的内容。这不会对 DataTables 提供的功能(如排序和过滤)有任何影响,因为 DataTables 会维护来自单元格的数据的内部缓存(以便更快地访问数据)——因此我们可以对 DOM 元素执行任何我们需要做的事情。
这部分的功能非常简单
function editRow ( oTable, nRow )
{
var aData = oTable.fnGetData(nRow);
var jqTds = $('>td', nRow);
jqTds[0].innerHTML = '<input type="text" value="'+aData[0]+'">';
jqTds[1].innerHTML = '<input type="text" value="'+aData[1]+'">';
jqTds[2].innerHTML = '<input type="text" value="'+aData[2]+'">';
jqTds[3].innerHTML = '<input type="text" value="'+aData[3]+'">';
jqTds[4].innerHTML = '<input type="text" value="'+aData[4]+'">';
jqTds[5].innerHTML = '[Save]()';
}
正如您所看到的,editRow() 函数采用两个参数
-
- oTable - DataTables 实例
-
- nRow - 要编辑的行对于的 TR 节点
对于行中可编辑的每个单元格,我们插入 input 标记,并通过使用 fnGetData 提取数据来设置其值(获取行数据)。请注意,“编辑”链接也会更新,显示“保存”,告诉终端用户再次单击链接时执行的操作。很明显这是个非常简单的案例,可轻松扩展以包括 select 元素或您希望使用的任何其他类型的输入。
function saveRow ( oTable, nRow )
{
var jqInputs = $('input', nRow);
oTable.fnUpdate( jqInputs[0].value, nRow, 0, false );
oTable.fnUpdate( jqInputs[1].value, nRow, 1, false );
oTable.fnUpdate( jqInputs[2].value, nRow, 2, false );
oTable.fnUpdate( jqInputs[3].value, nRow, 3, false );
oTable.fnUpdate( jqInputs[4].value, nRow, 4, false );
oTable.fnUpdate( '[Edit]()', nRow, 5, false );
oTable.fnDraw();
}
为了将用户编辑的信息保存回表中(以便可以像往常一样对其进行排序和筛选),我们使用 fnUpdate API 方法。fnUpdate 将写入给定的 TD 单元格节点,有效地使用新值替换在 editRow 函数中插入的 input 元素。请注意,传递给 fnUpdate 的第四个参数是 false,用于告诉 DataTables 不应重绘表,否则我们将在六次执行总体重绘,而实际上只需在所有更新完成后执行一次。
现在既然有了编辑和保存功能,我们需要向文档添加合适的事件处理程序来调用它们。为此,我们将 live 事件处理程序附加到编辑单元格中的 a 标记,它决定要执行的操作。单击“编辑”单元格时可能有三个状态
- 当前没有正在编辑的行
- 此行正在编辑,应保存
- 另一个正在编辑 - 应取消编辑并编辑此行
为了跟踪当前编辑的行,我们将一个名为 nEditing 的变量用于存储该行的引用 - 由此我们可以决定要处于哪种状态。结果,我们可以为初始化代码创建以下内容
$(document).ready(function() {
var oTable = $('#example').dataTable();
var nEditing = null;
$('#example a.edit').live('click', function (e) {
e.preventDefault();
/* Get the row as a parent of the link that was clicked on */
var nRow = $(this).parents('tr')[0];
if ( nEditing !== null && nEditing != nRow ) {
/* A different row is being edited - the edit should be cancelled and this row edited */
restoreRow( oTable, nEditing );
editRow( oTable, nRow );
nEditing = nRow;
}
else if ( nEditing == nRow && this.innerHTML == "Save" ) {
/* This row is being edited and should be saved */
saveRow( oTable, nEditing );
nEditing = null;
}
else {
/* No row currently being edited */
editRow( oTable, nRow );
nEditing = nRow;
}
} );
} );
添加行
复杂的部分已经解决 - 从这里开始全都是下坡路了!为了向表中添加新行,DataTables 提供了 fnAddData API 方法。我们可以将它与上述的 editRow 函数结合使用,创建新行并立即将其置于编辑模式中。这里我们将事件处理程序附加到表顶部的特殊链接上,位于文档准备函数中
$('#new').click( function (e) {
e.preventDefault();
var aiNew = oTable.fnAddData( [ '', '', '', '', '',
'[Edit]()', '[Delete]()' ] );
var nRow = oTable.fnGetNodes( aiNew[0] );
editRow( oTable, nRow );
nEditing = nRow;
} );
这里有两点需要考虑:首先,fnAddData 返回一个索引数组,每个索引都指向 DataTables 中为新行存储的行信息(它是一个数组,因为 fnAddData 一次可以添加多行)。由此索引,我们可以使用 fnGetNodes 获取 tr 元素以便进行编辑。其次,我们将 nEditing 变量设置为新行,以便编辑/保存处理程序知道在单击时应将其保存。
删除行
为了封装三个编辑函数,我们现在只需添加从表中删除行的选项,这可通过使用 DataTables 提供的 fnDeleteRow API 方法轻松实现 - 只需传入要删除的行引用
$('#example a.delete').live('click', function (e) {
e.preventDefault();
var nRow = $(this).parents('tr')[0];
oTable.fnDeleteRow( nRow );
} );
结论
本文介绍了一种可能的方法,用于构建 DataTables 中的 CRUD 界面。这里展示的实现故意保持简单,以此来展示 DataTables API 的用法,并显示如何快速构建和自定义这种界面来满足您的网站/应用程序需求。
值得注意的是,该实现有一些限制,还有一些领域有待改进
- 目前数据未保存到服务器,只在本地 DataTables 实例中保存 - 因此重新加载后,表将恢复到其最初状态。需要向服务器发出 XHR 调用,才能将用户输入信息保存到数据库中(在saveRow 中)。
- 列数以及哪些列可以编辑进行了硬编码 - 最好能够指定一个列索引数组,指出哪些列应该可编辑。
- 输入类型目前仅限于文本输入 - 最好有select 输入等选项。
- 可以实现自动聚焦和在“返回”时保存之类的 UI 改进,让最终用户操作起来更轻松。
我相信这是一个插件的基础 - 有人愿意接受挑战,将其构建成适用于 DataTables 的完整插件吗? :-)