I'm trying to display extra info on child rows without using ajax, the thing is that it works well but I want to display more than one value in a list way.
Something like this
Any suggestions?
I'm using web2py to take the data and fill the table, this is my try:
<script>
var tabla;
$(document).ready(function(){
tabla= $('#tablaGenerica').DataTable( {
} );
function format(value) {
return '<div>Hidden Value: ' + value + '</div>';
}
$('#tablaGenerica').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = tabla.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(tr.data('child-value'))).show();
tr.addClass('shown');
}
});
</script>
<table id="tablaGenerica" class="tablaC table-striped hover cell-border" cellspacing="0" width="100%" >
<thead>
<tr>
<th></th>
<th class="select-filter">Name</th>
<th class="select-filter">Age</th>
<th class="select-filter">Country</th>
<th class="select-filter">Level</th>
</tr>
</thead>
<tbody>
{{for person in formList:}}
<tr data-child-value="{{=person.Person.salary}}">
<td class="details-control"></td>
<td>{{=person.Person.name}}</td>
<td>{{=person.Person.age}}</td>
<td>{{=person.Person.country}}</td>
<td>{{=person.Person.level}}</td>
</tr>
{{pass}}
</tbody>
</table>
I'd suggest to refactor your app slightly, as you don't really need to cook up your HTML server-side, DataTables can handle that for you on the client end.
You may simply prepare the array of objects, where each entry corresponds to the table row and each object property / inner array element - to respective column (or details entry):
[
{id: 1, name: 'Clark Kent', age: 32, country: 'USA', level: 'high'},
{id: 2, name: 'Arthur Curry', age: 31, country: 'USA', level: 'medium'},
{id: 3, name: 'Barry Allen', age: 24, country: 'USA', level: 'low'}
]
In order to link those object properties / inner array items to your table columns, you may use DataTables option columns or columnDefs :
$('#tablaGenerica').DataTable({
...
columns: [
{title: 'ID', data: 'id'},
{title: 'Name', data: 'name'},
...
]
});
After that (and this is the essential part of the answer), in order to show multiple details within your child row, you simply need to modify your format() function, so that it returns HTML markup of the child row with all the necessary details:
const format = data => `
<div>Age: ${data.age}</div>
<div>Country: ${data.country}</div>
<div>Level: ${data.level}</div>
`;
So, complete DEMO of your case might look something, like that:
//specify source data
const dataSrc = [
{id: 1, name: 'Clark Kent', age: 32, country: 'USA', level: 'high'},
{id: 2, name: 'Arthur Curry', age: 31, country: 'USA', level: 'medium'},
{id: 3, name: 'Barry Allen', age: 24, country: 'USA', level: 'low'}
];
//initialize DataTables
const dataTable = $('#tablaGenerica').DataTable({
//specify necessary options to adjust DataTable to your needs
dom: 't',
data: dataSrc,
//specify columns that should be visible initially
columns: [
{title: 'ID', data: 'id'},
{title: 'Name', data: 'name'}
]
});
//declare function that renders child row with hidden details
const format = data => `
<div>Age: ${data.age}</div>
<div>Country: ${data.country}</div>
<div>Level: ${data.level}</div>
`;
//attach event listener to row click
$('#tablaGenerica').on('click', 'tr', function(){
//get clicked row into a variable
const clickedRow = dataTable.row($(this));
//show/hide child row
clickedRow.child.isShown() ? clickedRow.child.hide() : clickedRow.child(format(clickedRow.data())).show();
//toggle 'shown' class
$(this).toggleClass('shown');
});
#tablaGenerica tbody tr.even:hover, #tablaGenerica tbody tr.odd:hover{
cursor: pointer;
background: lightgray;
}
<!doctype html>
<html>
<head>
<script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
</head>
<body>
<table id="tablaGenerica"></table>
</body>
</html>
there is an example for you
/* Formatting function for row details - modify as you need */
function format ( d ) {
// `d` is the original data object for the row
return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Full name:</td>'+
'<td>'+d.name+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extension number:</td>'+
'<td>'+d.extn+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extra info:</td>'+
'<td>And any further details here (images etc)...</td>'+
'</tr>'+
'</table>';
}
$(document).ready(function() {
var data = [{
"id": "1",
"name": "Tiger Nixon",
"position": "System Architect",
"salary": "$320,800",
"start_date": "2011/04/25",
"office": "Edinburgh",
"extn": "5421"
},
{
"id": "2",
"name": "Garrett Winters",
"position": "Accountant",
"salary": "$170,750",
"start_date": "2011/07/25",
"office": "Tokyo",
"extn": "8422"
}];
var table = $('#example').DataTable( {
"data": data,
"columns": [
{
"className": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "salary" }
],
"order": [[1, 'asc']]
} );
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = table.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');
}
} );
} );
td.details-control {
background: url('https://datatables.net/examples/resources/details_open.png') no-repeat center center;
cursor: pointer;
}
tr.shown td.details-control {
background: url('https://datatables.net/examples/resources/details_close.png') no-repeat center center;
}
<link href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
</tr>
</tfoot>
</table>
Related
I have a Datatable with childrow:
$(document).ready(function () {
var table = $('#example').DataTable({
ajax: 'static/ajax/data/data.json',
columns: [
{
className: 'dt-control',
orderable: false,
data: null,
defaultContent: '',
},
{ data: 'Value1' },
{ data: 'Value2' },
{ data: 'Value3' },
],
order: [[1, 'asc']],
});
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.dt-control', function () {
var tr = $(this).closest('tr');
var row = table.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');
}
});
How can I make the child row always open without a performance hit and remove the default green cross? I'm using ajax as a data source.
To create a DataTable with all child data always displayed, you can take the standard approach (for example, using the demo shown here), and then make the following changes:
1 - remove the first (empty) column from the header <th></th> - and also from the footer if needed.
2 - remove the first body column also, as defined in the DataTable itself:
{
className: 'dt-control',
orderable: false,
data: null,
defaultContent: '',
},
The above 2 changes ensure there still the same number of body columns as there are header (and footer) columns.
3 - remove the event listener code (it is no longer needed):
$('#example tbody').on('click', 'td.dt-control', function () { ... });
4 - Add an initComplete section to the DataTable:
initComplete: function( settings, json ) {
this.api().rows().every( function () {
this.child( format(this.data()) ).show();
});
}
The following example shows the above approach, but it uses a small JavaScript-sourced data set instead of Ajax data.
That does not make a difference to the above steps. Just replace my data: dataSet, option with your ajax option.
var dataSet = [
{
"id": "1",
"name": "Tiger Nixon",
"position": "System Architect",
"salary": "$320,800",
"start_date": "2011/04/25",
"office": "Zürich",
"extn": "5421"
},
{
"id": "57",
"name": "Donna Snider",
"position": "Customer Support",
"salary": "$112,000",
"start_date": "2011/01/25",
"office": "New York",
"extn": "4226"
}
];
/* Formatting function for row details - modify as you need */
function format ( d ) {
// `d` is the original data object for the row
return '<table class="cell-border" cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Full name:</td>'+
'<td>'+d.name+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extension number:</td>'+
'<td>'+d.extn+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extra info:</td>'+
'<td>And any further details here (images etc)...</td>'+
'</tr>'+
'</table>';
}
$(document).ready(function() {
var table = $('#example').DataTable( {
data: dataSet,
columns: [
{ data: "name" },
{ data: "position" },
{ data: "office" },
{ data: "salary" }
],
initComplete: function( settings, json ) {
this.api().rows().every( function () {
this.child( format(this.data()) ).show();
});
}
} );
} );
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
</tr>
</thead>
</table>
</div>
</body>
</html>
an empty table. To fill the product table, its content should be created dynamically by using JavaScript to insert the data into the table. The data should be requested from the webserver. You should first send an AJAX GET request to the Web service. When this request returns successfully, you should insert the returned JSON data into your table using the DOM
You can try datatable plugin to fullfill your scenario
to work with this your data should be in the format of
{ "draw": 1, "recordsTotal": 57, "recordsFiltered": 57, "data": [
[
"Airi",
"Satou",
"Accountant",
"Tokyo",
"28th Nov 08",
"$162,700"
],
[
"Angelica",
"Ramos",
"Chief Executive Officer (CEO)",
"London",
"9th Oct 09",
"$1,200,000"
],
]
}
HTML CODE
<table id="example" class="display" style="width:100%">
<thead>
<tr>
<th>First name</th>
<th>Last name</th>
<th>Position</th>
<th>Office</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<table>
JS CODE:
$(document).ready(function() {
$('#example').DataTable( {
"ajax": "../server_side/scripts/server_processing.php"
} );
} );
include below scripts too
https://code.jquery.com/jquery-3.3.1.js
https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js
You can append the dynamically created html using innserHTML property ofDOM element.
Example
fetch('<some URL>')
.then((response) => {
let data = response.json(); // Let supposed the data is in this format [{ id: 1 }, { id: 2 }, { id: 3 }]
let tr = '';
data.forEach(function(value) {
tr += `<tr><td>${data.id}</td></tr>`;
});
document.querySelector('#table_id tbody').innerHTML = tr; //Append the data
}).catch(error => {
console.log(error);
});
Or use document.createElement to create the element and then append it to the DOM
fetch('<some URL>')
.then((response) => {
let data = response.json(); // Let supposed the data is in this format [{ id: 1 }, { id: 2 }, { id: 3 }]
let tr = '';
let tableBody = document.querySelector('#table_id');
data.forEach(function(value) {
let tr = document.createElement('tr');
tr.textContent = data.id
tableBody.appendChild(tr);
});
}).catch(error => {
console.log(error);
});
HTML
<table id="table_id">
<tbody>
</tbody>
</table>
I would like to display items belonging to different customers. To this effect, i am fetching data through an ajax call and therefater grouping the data based on each unique customer. I then append the grouped data to my html.
Structure of my grouped data looks like:
"John Doe": [
{
"Item_id" : 1
"Item_name": "abc"
},
{
"Item_id" : 2
"Item_name": "def"
},
],
"Jane Doe":
{
"Item_id" : 3
"Item_name": "ghi"
},
{
"Item_id" : 4
"Item_name": "jkl"
},
]
My code looks like:
$.each(groupedData, function (key, value) {
$('.cust_items').append(`
<h4 class="mb-0"> ` + key + `</h4>
<table id="dataTable">
<thead>
<th>Item No.</th>
<th>Item Name</th>
</thead>
<tbody></tbody>
</table>
`);
$.each(value, function (ky, val) {
$('#dataTable tbody').append(
`<tr>
<td>
` + ky + `
</td>
<td>
` + val.Item_name + `
</td>
</tr>
`);
});
});
I am facing an inssue whereby all items are being displayed under the first customer while the data is being displayed correctly under the second customer.
You're missing commas after the properties and Jane Doe property's objects aren't enclosed in []. Consider changing your groupedData object as the syntax isn't right.
Edit: Also adjusted the template string and accessing the table with dynamic id as well.
Sample:
let groupedData = {
"John Doe": [{
"Item_id": 1,
"Item_name": "abc"
},
{
"Item_id": 2,
"Item_name": "def"
}
],
"Jane Doe": [{
"Item_id": 3,
"Item_name": "ghi"
},
{
"Item_id": 4,
"Item_name": "jkl"
}
]
};
$.each(groupedData, function(key, value) {
$('.cust_items').append(`
<h4 class="mb-0">${key}</h4>
<table id="dataTable_${key.split(' ').join('_')}">
<thead>
<th>Item No.</th>
<th>Item Name</th>
</thead>
<tbody></tbody>
</table>
`);
$.each(value, function(ky, val) {
$(`#dataTable_${key.split(' ').join('_')} tbody`).append(
`<tr>
<td>
${ky}
</td>
<td>
${val.Item_name}
</td>
</tr>
`);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cust_items">
</div>
I'm a bit late to the party, but here's a fun and practical way to display your data dynamically using the <template> element and vanilla JS.
The in-code comments should make clear how it works.
Note: Be wary of adding the id attribute to elements inside a template (or inside a loop for that matter) cuz duplicate id values are bad.
const data = {
"John Doe" :
[
{ "Item_id" : 1, "Item_name": "abc" },
{ "Item_id" : 2, "Item_name": "def" }
],
"Jane Doe" : [
{ "Item_id" : 3, "Item_name": "ghi" },
{ "Item_id" : 4, "Item_name": "jkl" }
]
};
// Identifies HTML elements and an array of the names
const
container = document.getElementsByClassName("cust_items")[0],
template = document.getElementById("group_template"),
names = Object.keys(data);
// Loops through the names and adds a new copy of the template's contents for each one
for (let name of names){
// Identifies HTML elements (Technically, some are `document fragments`)
const
copyOfTemplate = document.importNode(template.content, true),
header = copyOfTemplate.querySelector(".header"),
tbody = copyOfTemplate.querySelector("tbody");
// Sets the header for the new copy of the template's contents
header.innerHTML = name;
// Loops through the items for this name and makes a new row for each one
for(let item of data[name]){
// Identifies strings and new HTML elements
const
itemId = item["Item_id"],
itemName = item["Item_name"],
row = document.createElement("TR"),
idCell = document.createElement("TD"),
nameCell = document.createElement("TD");
// Sets the item number and item name in their respective cells
idCell.innerHTML = itemId;
nameCell.innerHTML = itemName;
// Adds the cells to the new row
row.appendChild(idCell);
row.appendChild(nameCell);
// Adds the new row to the `tbody` within the new copy of the template's contents
tbody.appendChild(row);
}
// The new copy is ready to go live -- adds it to the page
container.appendChild(copyOfTemplate);
}
.header{ font-size: 1.2em; margin-bottom: 0.3em; }
table{ margin-left: 0.3em; margin-top: 0; border-collapse: collapse; }
th, td{ padding: 0.3em 0.5em; }
<div class="cust_items"></div>
<template id="group_template">
<h4 class="header"></h4>
<table>
<thead>
<th>Item No.</th>
<th>Item Name</th>
</thead>
<tbody></tbody>
</table>
<hr />
</template>
I would like this demo to work with the filter, not removing entries whose children have the data that you are filtering for.
E.g. in the example if you filter for 5407 Airi Satou does not get deleted and maybe even the child data gets expanded.
HTML and JS
/* Formatting function for row details - modify as you need */
function format ( d ) {
// `d` is the original data object for the row
return '<div class="slider">'+
'<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Full name:</td>'+
'<td>'+d.name+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extension number:</td>'+
'<td>'+d.extn+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extra info:</td>'+
'<td>And any further details here (images etc)...</td>'+
'</tr>'+
'</table>'+
'</div>';
}
$(document).ready(function() {
var table = $('#example').DataTable( {
"ajax": "/examples/ajax/data/objects.txt",
"columns": [
{
"class": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "salary" }
],
"order": [[1, 'asc']]
} );
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = table.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
$('div.slider', row.child()).slideUp( function () {
row.child.hide();
tr.removeClass('shown');
} );
}
else {
// Open this row
row.child( format(row.data()), 'no-padding' ).show();
tr.addClass('shown');
$('div.slider', row.child()).slideDown();
}
} );
} );
CSS
td.details-control {
background: url('/examples/resources/details_open.png') no-repeat center center;
cursor: pointer;
}
tr.shown td.details-control {
background: url('/examples/resources/details_close.png') no-repeat center center;
}
div.slider {
display: none;
}
table.dataTable tbody td.no-padding {
padding: 0;
}
SOLUTION
In order for jQuery DataTables to search child rows you need to add data displayed in the child rows to the main table as hidden columns.
For example, you can add hidden column for extn data property using columns.visible option as shown below:
JavaScript:
"columns": [
{
"class": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "salary" },
{ "data": "extn", "visible": false }
],
HTML:
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
<th>Extn.</th>
</tr>
</thead>
DEMO
See this jsFiddle for code and demonstration.
I'm working to add child rows to a data table and am getting a "TypeError: undefined is not a function" for a line of code that works perfectly on a different table and page. Any ideas?
HTML:
<div class="table-responsive">
<h2 class="sub-header">Account Users <span class="glyphicon glyphicon-question-sign"></span></h2>
<table id="users_table" class="table table-striped embedded_table">
<thead>
<tr class="text-center">
<th></th>
<th>User Name</th>
<th>Full Name</th>
<th>User Type</th>
<th>Assigned Device</th>
<th>Date Added</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
Javascript/jQuery:
<script>
function format ( d ) {
var html = '<table id="child_table" class="text-right" cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Email Address:</td>'+
'<td>'+ d.email_address +'</td>'+
'<td>Time Zone:</td>'+
'<td>'+ d.timezone +'</td>'+
'</tr>'+
'<tr>'+
'<td>Create Date:</td>'+
'<td>'+ d.create_date +'</td>'+
'<td>Last Login:</td>'+
'<td>'+ d.last_login +'</td>'+
'</tr>'+
'</table>';
return html;
}
$(document).ready(function() {
username = "<?php echo($_SESSION["username"]); ?>";
userType = "<?php echo($_SESSION["user_type"]); ?>";
var table = $('#users_table').dataTable({
order: [1, 'asc'],
"ajax": {
"url": "/s/user_data.php",
"dataSrc" : ""
},
"language": {
"search": "Search: "
},
"columns": [
{"data": null, "class": "details-control", "orderable": false, "defaultContent": "", "width": "2%"},
{"data": "username", "name": "username", "width": "20%"},
{"data": "fullName", "name": "fullName", "width": "20%"},
{"data": "type", "name": "type", "width": "15%"},
{"data": "cal_color", "name": "cal_color", "width": "15%"},
{"data": "create_date", "type": "date", "name": "create_date", "visible": false},
{"data": "time_zone", "name": "time_zone", "visible": false},
{"data": "last_login", "type": "date", "name": "last_login", "visible": false},
{"data": "email_address", "name": "email_address", "visible": false},
{"data": "uid", "name": "uid", "visible": false}
]
});
// Add event listener for opening and closing details
$('#users_table').find('tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var td = $(this).closest('td');
var row = table.row(tr);
console.log(tr);
console.log(td);
console.log(row);
if(row.child.isShown())
{
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
td.removeClass('shown');
}
else
{
// Open this row
row.child(format(row.data())).show();
tr.addClass('shown');
td.addClass('shown');
}
});
});
The line of code that generates the error is as follows. It's under the comment "Add event listener for opening and closing details" in the bottom third of the script.
var row = table.row(tr);
Like I said, I'm using the same listener on another table and this line isn't an issue there. I've checked my punctuation multiple times and don't see any missing commas, semicolons, or quotes. You can see that I have 3 lines writing to the console log. Here's what I get if I comment out the offending line:
[tr.even, prevObject: n.fn.init[1], context: td.details-control, jquery: "1.11.0", constructor: function, selector: ""…]
[td.details-control, prevObject: n.fn.init[1], context: td.details-control, jquery: "1.11.0", constructor: function, selector: ""…]
I'm not a strong javascript or jQuery developer. All comments and suggestions are welcome.
Thanks.
I think you should replace
var table = $('#users_table').dataTable({...
by
var table = $('#users_table').DataTable({
The difference? Datable with a capital "D". Otherwise, you can't use the function table.row()
From the manual (https://datatables.net/manual/api), you can see:
It is important to note the difference between $( selector ).DataTable() and $( selector ).dataTable(). The former returns a DataTables API instance, while the latter returns a jQueryJS object. An api() method is added to the jQuery object so you can easily access the API, but the jQuery object can be useful for manipulating the table node, as you would with any other jQuery instance (such as using addClass(), etc.).