2014 年 9 月 9 日星期二

永久嵌入复选框

与编辑器一起使用的主要交互方法是其主灯箱输入表单、气泡编辑器或内联编辑,但完全有可能通过其 丰富的 API 构建自己的表单控件,以便利用编辑器。

我最近被问到是否可以在表中永久显示复选框,以显示布尔字段的状态,并且在单击一次时更新该字段。使用编辑器的 内联编辑选项,可以通过单击一次将复选框插入表中并调用 inline() 方法,但需要再单击一次才能更改值并提交表单。速度很快,但当你有布尔状态切换常见的数据时,单击一次更新可以提升你应用程序的交互。

在这篇文章中,我展示了如何使用编辑器的 API 触发数据更新,通过构建一个包含一列复选框的表,并在切换时立即写入数据库。此外,完整的行(包括布尔状态)仍可通过标准编辑器编辑选项进行编辑。

如果你热衷于跳转到结尾并查看成品,可以在 编辑器示例网站上看到在这篇文章中开发的正在运行的代码

起点

对于本帖中创建的应用示例,我将使用编辑器包附带的一个示例数据库表,因为它非常适合此用例。具体来说,users 表有一个二进制 active 字段,我们可以将其用于复选框列表。

作为本帖的起点,考虑以下编辑器和 DataTables 初始化(注意如果你不熟悉 DataTables 和 Editor,它们的各自手册,DataTables / Editor,都详细介绍了如何设置和配置这两个库)

Javascript

var editor = new $.fn.dataTable.Editor( {
    "ajax": "../php/checkbox.php",
    "table": "#example",
    "fields": [
        {
            label:     "Active:",
            name:      "active",
            type:      "checkbox",
            separator: "|",
            ipOpts:    [
                { label: '', value: 1 }
            ]
        },
        { label: "First name:", name:  "first_name" },
        { label: "Last name:",  name:  "last_name" },
        { label: "Phone:",      name:  "phone" },
        { label: "City:",       name:  "city" },
        { label: "Zip:",        name:  "zip" }
    ]
} );

$('#example').dataTable( {
    dom: "Tfrtip",
    ajax: "../php/checkbox.php",
    columns: [
        { data: "first_name" },
        { data: "last_name" },
        { data: "phone" },
        { data: "city" },
        { data: "zip" },
        { data: "active" }
    ],
    tableTools: {
        sRowSelect: "os",
        aButtons: [
            { sExtends: "editor_create", editor: editor },
            { sExtends: "editor_edit",   editor: editor },
            { sExtends: "editor_remove", editor: editor }
        ]
    }
} );

PHP

include( "DataTables.php" );

use
    DataTables\Editor,
    DataTables\Editor\Field,
    DataTables\Editor\Format,
    DataTables\Editor\Join,
    DataTables\Editor\Validate;

Editor::inst( $db, 'users' )
    ->fields(
        Field::inst( 'first_name' ),
        Field::inst( 'last_name' ),
        Field::inst( 'phone' ),
        Field::inst( 'city' ),
        Field::inst( 'zip' ),
        Field::inst( 'active' )
            ->setFormatter( function ( $val, $data, $opts ) {
                return ! $val ? 0 : 1;
            } )
    )
    ->process( $_POST )
    ->json();

你可以从上述代码中注意到表中有六列,并且每个字段都可以在编辑器中编辑。active 字段是 checkbox 类型,我们使用 separator 选项让编辑器将数据作为一个字符串提交,而不是像默认那样作为数组(在有多个复选框的情况下非常有用)。

在 PHP 中,唯一需要特别考虑的是复选框未选中时的处理。当复选框未选中时,浏览器不会提交值,因此我们使用 setFormatter 确保数据库看到的值为 01(参见 编辑器 PHP 手册)。

上述代码将成功运行并且我们的表格完全可编辑,但是我们现在来看如何在表格中添加永久的复选框。

显示输入复选框

没有任何数据呈现器(如上文情况)时,从数据库中读取的数据会被直接写入表格并且因而用户会看到这些数据。在active列的情况下,这将是01,但是我们想要显示一个复选框。为了实现这个目的,我们可以使用 DataTables 的列呈现选项:columns.render。具体来说,我们想要选中 display 数据类型并返回一个 HTML 复选框。对于其他数据类型(例如,sort),我们返回原始数据(有关详细信息,请参阅 正交数据)。这样可以确保该列仍可排序和搜索。

{
    data:   "active",
    render: function ( data, type, row ) {
        if ( type === 'display' ) {
            return '<input type="checkbox" class="editor-active">';
        }
        return data;
    },
    className: "dt-body-center"
}

注意,columns.className 选项还用来指定 dt-body-center 类,它内置在 DataTables 的默认样式表中,以便将复选框在列中对齐 - 了解有关内置样式选项的更多信息,请 参阅样式手册

接下来,我们需要设置复选框的选中状态。为了确保这始终与行的源数据同步(例如,如果该状态在 Editor lightbox 表单中发生更改),我们使用了 rowCallback 选项。这是一个回调函数,它对 DataTables 显示的每一行运行,因此我们可以使用微不足道的 jQuery 代码行来设置checked属性。

rowCallback: function ( row, data ) {
    // Set the checked state of the checkbox in the table
    $('input.editor-active', row).prop( 'checked', data.active == 1 );
}

最后,对于表格显示,我们需要认可这样一个事实:TableTools 行选择器在默认情况下将选择一行,如果行的任何部分(包括复选框等子元素)被单击。通过单击选中复选框是一个理想操作,但我们不希望因此而选择该行。尽管可以使用带事件监听器的 stopPropagation(),但是 TableTools 提供了一个更简单的方法 - sRowSelector 选项。

此属性提供了让 TableTools 了解对行选择使用哪个选择器的能力。在这种情况下,我们想要使用整行,除了最后一列。为此,我们可以使用一个简单的 CSS 选择器(注意,它被添加到了 tableTools 初始化对象中)

sRowSelector: 'td:not(:last-child)' // no row selection on last column

API 更新

在显示复选框后,此过程中的下一步也是最后一步是当状态改变时执行 Editor 操作。为此,我们使用 jQuery change 事件处理程序来调用 edit() 方法,以开始对该行的编辑。使用 edit() 的第二个参数,我们可以要求表单不显示(因为它会根据默认情况显示)。

然后,使用 set() 方法根据被切换的复选框将active状态值设为,最后,使用 submit() 方法将数据提交给服务器。

$('#example').on( 'change', 'input.editor-active', function () {
    editor
        .edit( $(this).closest('tr'), false )
        .set( 'active', $(this).prop( 'checked' ) ? 1 : 0 )
        .submit();
} );

就是这样!

运行示例

此示例可在 编辑器网站 上获得,如果您感兴趣,您可以在该网站上使用本帖中开发的代码,并对其进一步进行解析。

完整代码

为了全面起见,上面讨论的代码已合并到我们本帖开头的起点中,以展示完全组装且完成的工作代码

var editor = new $.fn.dataTable.Editor( {
    "ajax": "../php/checkbox.php",
    "table": "#example",
    "fields": [ {
            label:     "Active:",
            name:      "active",
            type:      "checkbox",
            separator: "|",
            ipOpts:    [
                { label: '', value: 1 }
            ]
        },
        { label: "First name:", name:  "first_name" },
        { label: "Last name:",  name:  "last_name" },
        { label: "Phone:",      name:  "phone" },
        { label: "City:",       name:  "city" },
        { label: "Zip:",        name:  "zip" }
    ]
} );

$('#example').dataTable( {
    dom: "Tfrtip",
    ajax: "../php/checkbox.php",
    columns: [
        { data: "first_name" },
        { data: "last_name" },
        { data: "phone" },
        { data: "city" },
        { data: "zip" },
        {
            data:   "active",
            render: function ( data, type, row ) {
                if ( type === 'display' ) {
                    return '<input type="checkbox" class="editor-active">';
                }
                return data;
            },
            className: "dt-body-center"
        }
    ],
    tableTools: {
        sRowSelect: "os",
        aButtons: [
            { sExtends: "editor_create", editor: editor },
            { sExtends: "editor_edit",   editor: editor },
            { sExtends: "editor_remove", editor: editor }
        ],
        sRowSelector: 'td:not(:last-child)' // no row selection on last column
    },
    rowCallback: function ( row, data ) {
        // Set the checked state of the checkbox in the table
        $('input.editor-active', row).prop( 'checked', data.active == 1 );
    }
} );

$('#example').on( 'change', 'input.editor-active', function () {
    editor
        .edit( $(this).closest('tr'), false )
        .set( 'active', $(this).prop( 'checked' ) ? 1 : 0 )
        .submit();
} );