2014 年 9 月 9 日星期二

永久內嵌核取方塊

Editor 使用的主要互動方法是其主要燈箱輸入表單、泡泡編輯器或內嵌編輯,但也可以透過其 廣泛的 API 建構您自己的表單控制項來使用 Editor。

我最近被問到是否有可能在表格中永久顯示核取方塊,以顯示布林值欄位的狀態,並在單擊切換時更新該欄位。使用 Editor 的 內嵌編輯選項,可以透過單擊在表格中插入核取方塊,並呼叫 inline() 方法,但是需要再次單擊才能變更值並提交表單。雖然快速,但是當您擁有布林值狀態切換很常見的資料時,單擊更新可以改善應用程式的互動性。

在這篇文章中,我將展示如何使用 Editor 的 API,透過建構具有核取方塊列的表格來觸發資料更新,並在切換時立即寫入資料庫。此外,整列 (包括布林值狀態) 將透過標準 Editor 編輯選項保持可編輯狀態。

如果您很想跳到最後並查看完成的產品,可以在 Editor 範例網站上實際執行查看這篇文章中開發的程式碼。

起點

對於這篇文章中建立的應用範例,我將使用 Editor 套件附帶的其中一個範例資料庫表格,因為它非常適合此使用案例。具體來說,users 表格有一個二進位 active 欄位,我們可以將其用於我們的核取方塊清單。

作為這篇文章的起點,請考慮以下 Editor 和 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();

您可以從上面的程式碼中注意到,表格中有六個欄位,而且每個欄位都可以在 Editor 中編輯。active 欄位是 checkbox 類型,我們使用 separator 選項讓 Editor 將資料作為字串提交,而不是像預設那樣作為陣列提交(適用於有多個核取方塊的情況)。

在 PHP 中,唯一需要特別考慮的是取消選取核取方塊的情況。當核取方塊未被選取時,瀏覽器不會提交值,因此我們使用 setFormatter 來確保使用 01 作為資料庫看到的值(請參閱 Editor 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 燈箱表單中變更了狀態),則使用 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();
} );

就是這樣!

執行範例

此範例可在 Editor 網站上取得,您可以在其中使用這篇文章中開發的程式碼,並在有興趣的情況下進一步解構它。

完整程式碼

為了完整起見,上述討論的程式碼會與這篇文章頂部的起點合併,以顯示完全組裝的完整工作程式碼

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();
} );