I have a table that makes use of child rows where each row is expandable / collapsible, but my parent rows will have duplicate data.
I would like to group my child rows or child row, as they are called in the official Datatables documentation , I have the following table where in the parent row there are the Purchase Order, Purchase Order Date, Currency and Status columns.
If you look, I have 3 purchase orders that correspond to the same identifier in this example is 258, but each purchase order contains a secondary row with different information, that information is Receipt Date, No. Invoice, Item Code and Description.
+-----------------------------------------------------------------------+
| | Purchase Order | Purchase Order Date |Currency| Status |
+----+------------------+--------------------------+--------+-----------+
| + | 258 | 06/01/2020 | USD | Delivered |
+------+---------+------------+--------------------+-------------+------+
| Receipt Date | No. Invoice | Code Item | Description |
+------+---------+-----------+---------------------+-------------+------+
| 07/01/2020 | 617 | CA0033 | CT |
+-----------------------------------------------------------------------+
| + | 258 | 06/01/2020 | USD | Delivered |
+-----------------------+--------------------------+--------+-----------+
| Receipt Date | No. Invoice | Code Item | Description |
+-----------------------+-------------+-----------------+---------------+
| 14/01/2020 | 620 | CA0036 | CTR |
+-----------------------+-------------+-----------------+---------------+
| + | 258 | 06/01/2020 | USD | Delivered |
+-----------------------+--------------------------+--------+-----------+
| Receipt Date | No. Invoice | Code Item | Description |
+-----------------------+-------------+-----------------+---------------+
| 16/01/2020 | 626 | CC0048 | CTY |
+-----------------------+-------------+-----------------+---------------+
What I would like to achieve without repeating the Purchase Order is to group the secondary rows as follows.
+-----------------------------------------------------------------------+
| | Purchase Order | Purchase Order Date |Currency| Status |
+----+------------------+--------------------------+--------+-----------+
| + | 258 | 06/01/2020 | USD | Delivered |
+------+---------+------------+-------------------+-------------+-------+
| Receipt Date | No. Invoice | Code Item | Description |
+------+---------+-----------+--------------------+-------------+-------+
| 07/01/2020 | 617 | CA0033 | CT |
+-----------------------+-------------+-----------------+---------------+
| 14/01/2020 | 620 | CA0036 | CTR |
+-----------------------+-------------+-----------------+---------------+
| 16/01/2020 | 626 | CC0048 | CTY |
+-----------------------+-------------+-----------------+---------------+
If you look at the Purchase Order now it contains the information of the same 3 orders grouped together, this is what I want to get to.
The following is the code of my AJAX call which I use to build my tables.
/* Formatting function for row details - modify as you need */
function format(d) {
// `d` is the original data object for the row
console.log(d);
return '<table cellpadding="5" cellspacing="0" style="border-collapse: separate; border-spacing: 40px 5px;">' +
'<tr>' +
'<td><strong>Receipt Date: </strong></td>' + '<td><strong>No. Invoice:<strong></td>' + '<td><strong>Code Item:<strong></td>' + '<td><strong>Description:</strong></td>' +
'</tr>' +
'<tr>' +
'<td>' + d.ReceiptDate + '</td>' + '<td>' + d.Invoice+ '</td>' + '<td>' + d.CodeItem+ '</td>' + '<td>' + d.Description + '</td>' +
'</tr>' +
'</table>';
}
$(document).ready(function () {
$('#example').dataTable( {
responsive : true,
ajax : {
"type": 'POST',
"url" : './test.php',
"dataType": 'JSON',
"cache": false,
"data": {
'param' : 1,
},
},
language : {
"lengthMenu": "Mostrar _MENU_ registros",
"zeroRecords": "No se encontró nada",
"info": "Mostrando del _START_ al _END_ de un total de _TOTAL_",
"infoEmpty": "No hay registros",
"emptyTable": "No hay datos para mostrar",
"loadingRecords": "Cargando...",
"processing": "Procesando...",
"search": "Buscar:",
"infoFiltered": "(filtrado de un total de _MAX_ registros)",
"paginate": {
"first": "Primera",
"last": "Última",
"next": "Siguiente",
"previous": "Anterior"
}
},
columns: [
{
"className": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data" : "PurchaseOrder" },
{ "data" : "PurcharOrderDate" },
{ "data" : "Currency" },
{ "data" : "Status" }
],
order : [[1, 'desc']]
} );
// Add event listener for opening and closing details
$('#example').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = $('#example').DataTable().row(tr);
if (row.child.isShown()) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Open this row
row.child(format(row.data())).show();
tr.addClass('shown');
}
});
});
Consulting the Datatables documentation it has a RowGroup extension but I don't know if they can be grouped for child rows or child rows. I would be grateful to each of you if you can help me find a way out of this problem.
UPDATE:
Below I denormalize my JSON response provided by test.php as requested in the comment, this is done with the goal of getting access to all the data you want for each child row.
[
{
"Purchase Order": 949,
"Purchase Order Date": "20/11/2019",
"Receipt Date": "12/12/2019",
"No. Invoice": 448,
"Code Item": "CC0048",
"Description": "CTR",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 949,
"Purchase Order Date": "20/11/2019",
"Receipt Date": "13/12/2019",
"No. Invoice": 448,
"Code Item": "CC0048",
"Description": "CTR",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 949,
"Purchase Order Date": "20/11/2019",
"Receipt Date": "14/12/2019",
"No. Invoice": 448,
"Code Item": "CC0048",
"Description": "UBC",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 949,
"Purchase Order Date": "20/11/2019",
"Receipt Date": "15/12/2019",
"No. Invoice": 448,
"Code Item": "CC0048",
"Description": "UBC",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 258,
"Purchase Order Date": "05/12/2019",
"Receipt Date": "17/12/2019",
"No. Invoice": 451,
"Code Item": "CA0033",
"Description": "CTY",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 258,
"Purchase Order Date": "05/12/2019",
"Receipt Date": "18/12/2019",
"No. Invoice": 451,
"Code Item": "CA0033",
"Description": "CTY",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 258,
"Purchase Order Date": "05/12/2019",
"Receipt Date": "19/12/2019",
"No. Invoice": 452,
"Code Item": "CA0033",
"Description": "CTY",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 258,
"Purchase Order Date": "05/12/2019",
"Receipt Date": "20/12/2019",
"No. Invoice": 452,
"Code Item": "CA0033",
"Description": "CTY",
"Status": "Delivered",
"Currency": "USD"
},
{
"Purchase Order": 258,
"Purchase Order Date": "05/12/2019",
"Receipt Date": "21/12/2019",
"No. Invoice": 452,
"Code Item": "CA0033",
"Description": "CTY",
"Status": "Delivered",
"Currency": "USD"
}
]
It is important to remember that as parent row I require Purchase Order, Purchase Order Date, Currency and Status and as child row I require to locate Receipt Date, No. Invoice, Code Item and Description.
UPDATE 2:
I add php code to give a little more guidance to my question.
Test.php
<?php
header('Content-Type: text/html; charset=utf-8');
$param = $_POST['param'];
switch($param) {
case '1':
$query = array();
include './db/conecct.php';
$sql = "select PURCHID as 'PurchaseOrder',
CREATEDDATETIME as 'PurchaseOrderDate',
MONEDA as 'Currency',
INVOICEDATE as 'ReceiptDate',
ITEMID as 'CodeItem',
FACTURA as 'No. Invoice',
NAMEALIAS as 'Description',
PURCHSTATUS as 'Status'
FROM PP_FACTURAS
$stmt = sqlsrv_query($conn, $sql, $params);
if ( $stmt === false) {
die( print_r( sqlsrv_errors(), true) );
}
while( $row = sqlsrv_fetch_array($stmt) ) {
//print_r($row);
$record = array(
"PurchaseOrder" => $row['PurchaseOrder'],
"PurchaseOrderDate" => $row['PurchaseOrderDate']->format('d/m/Y'),
"Currency" => $row['Currency'],
"Status" => $row['Status'],
"PurchaseOrderDate" => $row['PurchaseOrderDate'] != null ? $row['PurchaseOrderDate']->format('d/m/Y'):"",
"No. Invoice" => utf8_encode ($row['No. Invoice']),
"CodeItem" => utf8_encode ($row['CodeItem']),
"Description" => utf8_encode ($row['Description']),
);
array_push($query, $record);
}
sqlsrv_free_stmt( $stmt);
sqlsrv_close($conn);
$json = array(
"success"=> count($query) > 0 ? true : false,
"data"=>$query
);
echo json_encode($json);
break;
UPDATE 3:
Try to do the answer and now I get the following error in console:
Uncaught TypeError: originalJson is not iterable
I attach the code with which I am supporting myself according to the answer:
/* Formatting function for row details - modify as you need */
function format(d) {
// `d` is the original data object for the row
console.log(d);
var tableHtml = '<table><thead><tr><th>Receipt Date</th><th>Invoice No.</th><th>Item Code</th><th>Description</th></tr></thead>';
tableHtml = tableHtml + '<tbody>';
var rowHtml = '';
for (const rowData of d.details){
rowHtml = rowHtml + '<tr><td>' + rowData.ReceiptDate + '</td><td>' + rowData.Invoice + '</td><td>' + rowData.CodeItem + '</td><td>' + rowData.Description + '</td></tr>';
}
tableHtml = tableHtml + rowHtml + '</tbody></table>';
return tableHtml;
}
function denormalize(originalJson) {
let denormalizedMap = new Map();
for (const element of originalJson) {
let headerInfo = (({
PurchaseOrder,
PurcharOrderDate,
Currency,
Status
}) => ({
PurchaseOrder,
PurcharOrderDate,
Currency,
Status
}))(element);
headerInfo.details = [];
let detailLine = (({
ReceiptDate,
Invoice,
CodeItem,
Description
}) => ({
ReceiptDate,
Invoice,
CodeItem,
Description
}))(element);
if (! denormalizedMap.has(element.PurchaseOrder)) {
denormalizedMap.set(element.PurchaseOrder, headerInfo);
}
denormalizedMap.get(element.PurchaseOrder).details.push(detailLine);
}
let denormalizeSource = Array.from(denormalizedMap.values());
return denormalizeSource;
}
$(document).ready(function () {
$('#example').dataTable( {
responsive : true,
ajax : {
"type": 'POST',
"url" : './test.php',
"dataType": 'JSON',
"cache": false,
"dataSrc" : function (json){
console.log(json);
return denormalize(json);
},
},
columns: [
{
"className": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data" : "PurchaseOrder" },
{ "data" : "PurcharOrderDate" },
{ "data" : "Currency" },
{ "data" : "Status" }
],
order : [[1, 'desc']],
} );
// Add event listener for opening and closing details
$('#example').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = $('#example').DataTable().row(tr);
if (row.child.isShown()) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Open this row
row.child(format(row.data())).show();
tr.addClass('shown');
}
});
});

dis the original data object for the row"). You could denormalize your JSON response provided bytest.phpfirst (or post-process it using JavaScript), to get access to all the data you want for each child row. Then,dwill contain all the data you need. This also solves the "duplicate parent rows" issue at the same time (no duplicate PO rows).