父/子行編輯在子行中
各位新年快樂。去年我們將重心放在 DataTables 的其他方面、擴充功能和支援,因此部落格文章略有減少,但在 2019 年我們會更頻繁地發布部落格文章。首先,我們要重新探討Editor 的父/子文章。當您有一對多的資料庫結構時,父/子編輯是一個相當熱門的主題,它可讓終端使用者在單一頁面上編輯兩個表格的資料。
關於先前文章最常見的問題是「我們如何用子行而不是總是顯示子表格來完成此操作?」這就是我們在此要探討的內容。快速入門,這就是我們的目標結果
名稱 | 使用者 |
---|
父表格
我們將從第一原則建立此處的編輯表格,而不是嘗試修改先前的父/子編輯文章,這樣更容易理解。該過程的第一步是建立父表格,這是一個非常簡單的 Editor 和 DataTable 組合,您可以在Editor 範例中找到,我們也會將其與row details DataTables 範例結合。
Editor Javascript
Editor Javascript 非常簡單,只有一個欄位(網站名稱),它會將資料提交到伺服器端腳本
var siteEditor = new $.fn.dataTable.Editor( {
ajax: '../php/sites.php',
table: '#sites',
fields: [ {
label: 'Site name:',
name: 'name'
} ]
} );
DataTables Javascript
對於 DataTables 初始化,我們需要定義三個欄位
- 子行的顯示/隱藏控制項
- 網站名稱
- 分配給該網站的使用者人數。為此,使用一個
columns.render
函式,它只會從資料陣列中返回使用者人數。
var siteTable = $('#sites').DataTable( {
order: [ 1, 'asc' ],
ajax: '../php/sites.php',
columns: [
{
className: 'details-control',
orderable: false,
data: null,
defaultContent: '',
width: '10%'
},
{ data: 'name' },
{ data: 'users', render: function ( data ) {
return data.length;
} }
],
select: {
style: 'os',
selector: 'td:not(:first-child)'
},
layout: {
topStart: {
buttons: [
{ extend: 'create', editor: siteEditor },
{ extend: 'edit', editor: siteEditor },
{ extend: 'remove', editor: siteEditor }
]
}
}
} );
另請注意,select.selector
選項用於禁止選取子行顯示/隱藏控制項欄,您不希望使用者每次顯示或隱藏子行時都變更列選取!
伺服器端 (PHP)
對於父表格,PHP 腳本會從網站表格讀取 id
和 name
欄。當選取列時,需要 id
欄才能將資訊提交到子表格伺服器端腳本,id
實際上不會顯示在表格中,也不在 Editor 表單中(因此為了安全起見,會使用 set(false)
)。
這裡值得注意的一點是,會使用 Mjoin
執行個體來取得使用每個網站的項目數的相關資訊 (Mjoin
是「多重聯結」的縮寫)。有關如何使用 Mjoin
的詳細說明,請參閱Editor 手冊。如果您不需要或不想在父表格中顯示計數欄,則不需要 Mjoin
。
Editor::inst( $db, 'sites' )
->fields(
Field::inst( 'id' )->set( false ),
Field::inst( 'name' )->validator( 'Validate::notEmpty' )
)
->join(
Mjoin::inst( 'users' )
->link( 'sites.id', 'users.site' )
->fields(
Field::inst( 'id' )
)
)
->process( $_POST )
->json();
子表格
現在,讓我們撰寫將顯示和隱藏子行的事件處理常式,因為這會定義顯示和隱藏每個子行的 DataTable 所需的函式。這是對row details 範例的小幅修改。我們不會僅將列資料傳遞到建立子表格的方法,而是傳遞整個列執行個體,讓使用者可以存取父表格的完整 DataTables API。此外,在這種情況下,我們將使用銷毀函式來清理關閉時的子表格,以確保沒有記憶體洩漏
$('#sites tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = siteTable.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
destroyChild(row);
tr.removeClass('shown');
}
else {
// Open this row
createChild(row);
tr.addClass('shown');
}
} );
由此,我們需要建立兩個函式
- 用於子表格的 DataTable 和 Editor 功能的
createChild
- 用於清理的
destroyChild
建立 DataTable
在row details 範例中,會將字串提供給 child()
方法,以便在子行中顯示。但是,也可以傳遞 DOM 元素,因此我們只需建立表格元素、將其插入到文件中(使用child().show()
),然後將其初始化為常規的 DataTable,即可建立 DataTable
function createChild ( row ) {
// This is the table we'll convert into a DataTable
var table = $('<table class="display" width="100%"/>');
// Display it the child row
row.child( table ).show();
// Initialise as a DataTable
var usersTable = table.DataTable( {
// ...
} );
}
您將可從此處看到,每當使用者要求顯示子行時,我們就可以動態建立任何 DataTable。每次呼叫 createChild()
函式時,都會建立新的唯一表格,因此也無需為每個表格建立 ID。
銷毀 DataTable
當需要關閉子行時,我們不希望只是關閉它並將 DataTable 留在其中佔用記憶體,如果終端使用者開啟和關閉足夠多的列,這將會導致記憶體洩漏,最終導致瀏覽器記憶體不足。相反,我們需要使用 destroy()
方法來銷毀表格及其所有事件處理常式。我們也會使用一小段 jQuery 將其從 DOM 中移除
function destroyChild(row) {
var table = $("table", row.child());
table.detach();
table.DataTable().destroy();
// And then hide the row
row.child.hide();
}
Editor 設定
Editor 子行的設定與任何其他基本 Editor幾乎完全相同,它定義了資料的 Ajax URL、要編輯的表格和要編輯的欄位。但是,在這種情況下,我們需要使用 ajax.data
選項來將父系的 ID(在此情況下為網站 ID)傳送到伺服器,以便在 WHERE
條件中使用。我們也可以透過使用設定為父列 ID 的 field.def
選項(在此情況下為 rowData.id
)來預選子表格所在的網站,使使用者體驗更輕鬆
var rowData = row.data();
var usersEditor = new $.fn.dataTable.Editor( {
ajax: {
url: '/media/blog/2016-03-25/users.php',
data: function ( d ) {
d.site = rowData.id;
}
},
table: table,
fields: [ {
label: "First name:",
name: "users.first_name"
}, {
label: "Last name:",
name: "users.last_name"
}, {
label: "Phone #:",
name: "users.phone"
}, {
label: "Site:",
name: "users.site",
type: "select",
placeholder: "Select a location",
def: rowData.id
}
]
} );
DataTable 設定
DataTable 設定也與其他基本 DataTables 幾乎相同,再次修改為使用 ajax.data
來傳送父列的 ID,以確保只載入屬於該父系的列
var usersTable = table.DataTable( {
pageLength: 5,
ajax: {
url: '/media/blog/2016-03-25/users.php',
type: 'post',
data: function ( d ) {
d.site = rowData.id;
}
},
columns: [
{ title: 'First name', data: 'users.first_name' },
{ title: 'Last name', data: 'users.last_name' },
{ title: 'Phone #', data: 'users.phone' },
{ title: 'Location', data: 'sites.name' }
],
select: true,
layout: {
topStart: {
buttons: [
{ extend: 'create', editor: usersEditor },
{ extend: 'edit', editor: usersEditor },
{ extend: 'remove', editor: usersEditor }
]
}
}
} );
我們還使用pageLength
來保持子行中的頁面大小較小,但可以根據需要進行設定。事實上,這突顯了子行中的 Editor 和 DataTable 只是常規元件,可以使用每個元件的任何選項、事件和 API 進行修改,就像任何其他 Editor 和 DataTable 一樣。
更新父表格
當子表格被修改時,父表格的使用者計數可能需要在一個或多個列中更新。為此,我們使用 ajax.reload()
方法(透過我們傳遞到函式中的 row
變數存取 - 回想一下,DataTables 的鏈結 API 允許在所有層級存取最上層方法)
usersEditor.on( 'submitSuccess', function (e, json, data, action) {
row.ajax.reload(function () {
$(row.cell( row.id(true), 0 ).node()).click();
});
} );
上面的第三行使用合成的 click
事件來觸發子行的「顯示」動作,因為 Ajax 重新載入會導致它自動關閉(它實際上是新增的列)。可以從伺服器取得新的計數,並使用 row().data()
更新每個列的資料,以提高效率,但這不在本文的範圍內。
伺服器端 (PHP)
儘管在用戶端上,我們可以同時初始化並顯示多個子 Editor,但在伺服器端,我們只需要一個腳本,它會透過父 ID(即網站)來區分 Editor。這是使用提交的 site
參數和 WHERE
條件來完成。
if ( ! isset($_POST['site']) || ! is_numeric($_POST['site']) ) {
echo json_encode( [ "data" => [] ] );
}
else {
Editor::inst( $db, 'users' )
->field(
Field::inst( 'users.first_name' ),
Field::inst( 'users.last_name' ),
Field::inst( 'users.phone' ),
Field::inst( 'users.site' )
->options( 'sites', 'id', 'name' )
->validator( 'Validate::dbValues' ),
Field::inst( 'sites.name' )
)
->leftJoin( 'sites', 'sites.id', '=', 'users.site' )
->where( 'site', $_POST['site'] )
->process($_POST)
->json();
}
將其組合在一起
只剩下一個步驟 - 當父表格中的網站標籤更新時,我們需要在子表格中反映這一點。我們可以使用子表格的 ajax.reload()
函式來完成此操作
function updateChild ( row ) {
$('table', row.child()).DataTable().ajax.reload();
}
可以使用以下程式碼呼叫該函式
siteEditor.on('submitSuccess', function () {
siteTable.rows().every(function () {
if (this.child.isShown()) {
updateChild(this);
}
});
} );
如果您想查看本文中使用的完整組合 Javascript,則請在此處取得。也可以取得所用的 CSS,雖然這只是用於列詳細資料按鈕和強調子行。
從這裡開始
我希望您能從這篇文章中了解,子行的顯示不應僅限於基本範例中的靜態資料。您可以在子行中新增任何您想要的互動,包括全面的可編輯 DataTable!