Child row datatables - row data value undefined - javascript

I have a datatable with child rows populated by the following function:
$('#myDataTable tbody').on('click', 'td.details-control', function () {
console.log(table.row(this).data());
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');
}
});
The format function:
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><strong>BLABLA1</td>' +
'<td align="center">'+
d.BLABLA1 +
'</td>'+
'<td><strong>BLABLA2</td>' +
'<td align="center">'+
d.BLABLA2+
'</td>'+
'</tr>' +
'</table>';
}
However, when I display the page, and I expand the child rows, the values of the columns are "undefined"...
The rows in the datatable are populated from a model (Using MVC 4) as follows:
<tbody>
#foreach (var item in Model)
{
<tr>
<td align="center">
#Html.DisplayFor(modelItem => item.BLABLA3)
</td>
.....</tr>}
</tbody>
I think I am missing the method to get the row.data() loaded into the formatting function.... What is the correct way of doing so? I already checked all the documentations and they all use what I tried....

Yes, it's strange the page https://datatables.net/examples/api/row_details.html explain that we have to write :
'<td>'+d.myrow+'</td>'+
But if i do this, row's value is "undefined", so i have to write :
'<td>'+d["myrow"]+'</td>'+
And it's works !

Apparently I had to select the index of the object/array returned by .data() for every row, the following code worked for me.....(replace KNOWN_INDEX by integer index of the child row...)
<tr>'+
'<td><strong>BLABLA1</td>' +
'<td align="center">'+
d[KNOWN_INDEX]+
'</td>'+
.
.
.
'</tr>'+

Related

Why does Table Row element closes automatically?

I am creating a table that has api data.
This is my js file fetching data:
$(document).ready(function(){
$.getJSON(endpoint, function(data){
var topAgent = [];
$.each(data, function(key, value){
topAgent.push ("<tr>");
topAgent.push ("<td>" + value.agentId + "</td>");
topAgent.push ("<td>" + value.firstName + "</td>");
topAgent.push ("</tr>");
});
$('#topAgents').append(topAgent);
console.log(topAgent);
});
});
When I am checking the console log, it seems fine.
0: "<tr>"
1: "<td>5</td>"
2: "<td>Glenn</td>"
3: "</tr>"
4: "<tr>"
5: "<td>6</td>"
6: "<td>Glenell</td>"
7: "</tr>"
But when I inspect element, the table row element closes automatically:
<tr></tr>
<td>5</td>
<td>Glenn</td>
<tr></tr>
<tr></tr>
<td>6</td>
<td>Glenell</td>
<tr></tr>
I tried to remove the table row element in the js file but the outcome is all the data are aligned together. I also tried to combine the table row element along with the Table Data Cell element but it still automatically closes.
What could cause the problem and what are the possible solutions?
The line topAgent.push ("</tr>"); is causing an issue because since it is inside .each() method <tr> will get close everytime after the data gets filled inside value.agentId and value.firstName
Use append instead:
$(function() {
$.each(data, function(key, value){
var $tr = $('<tr>').append(
$('<td>').text(value.agentId),
$('<td>').text(value.firstName)
);
$('#topAgents').append(topAgent);
});
});
Inspired from: https://stackoverflow.com/a/17724264/6029001

Using Index in forEach Array

I have an array of objects being displayed in a table... My goal is to access a specific item within the array by clicking on that item in the table. I would then be able to add/remove classes and access the values, which is ultimately what I need to do.
Here's where I'm stuck...
myArray.forEach((item, index) => {
// Sort through array, render to DOM
document.getElementById('myElementID').innerHTML +=
'<tr>' +
'<td>' +
item.thing +
'</td>' +
'<td' +
item.thing2 +
'</td>' +
'</tr>';
// Completely stuck... I've added an event listener to each table row.
addEventListener('dblclick', () => {
console.log(//I want to log the index of the item I just clicked on);
});
});
Please forgive me if this is very easy or I'm going about this all wrong, but I'm very new to all of this and I haven't been able to structure my question in such a way that google is helpful.
Thanks in advance.
EDIT - Some html as requested...
<table id="myElementID">
<tr>
<th id="heading">Heading1</th>
<th id="anotherHeading">Heading2</th>
</tr>
</table>
EDIT again (sorry) ... and a JS fiddle. You'll see that it logs both indexes, instead of just the one I clicked on. https://jsfiddle.net/c4pd5wmg/4/
Instead of messing with index etc.. you can attach the event handler to the tr and just reference e.target in the event handler. I also cleaned up your adding of tr.
const myArray= [{number: 45,otherNumber: 55},{number: 48,otherNumber:58}]
myArray.forEach((item, index) => {
let row = document.createElement("tr");
let cell = document.createElement("td");
cell.innerHTML = item.number;
row.appendChild(cell);
cell = document.createElement("td");
cell.innerHTML = item.otherNumber;
row.appendChild(cell);
document.getElementById('myElementID').appendChild(row);
row.addEventListener('dblclick', (e) => {
console.log(e.target);
});
});
<table id="myElementID">
<tr>
<th id="heading">Heading1</th>
<th id="anotherHeading">Heading2</th>
</tr>
</table>

Datatables add multiple child-rows

I have a table hooked up to the jQuery datatable. Based on this example I want to add "hidden" child rows to show extra information.
I have the following jsfiddle where I have a name/value pair.
<tr data-child-name="Name1" data-child-value="Value 1">
<td class="details-control"></td>
function format ( name, value ) {
return '<div>' + name + ': '+ value + '</div>';
}
$(document).ready(function () {
var table = $('#example').DataTable({});
// Add event listener for opening and closing details
$('#example').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(tr.data('child-name'), tr.data('child-value') )).show();
tr.addClass('shown');
}
});
});
My question is how do I add more than one name/value pair? So that I can define various rows like in the datatables.net example.
My source is a php-script that generates html like in the jsfiddle example.
It's probably an easy task but my jQuery skills are very limited :-)
Thank you.
UPDATE:
The data comes from an ldap query:
$ldapResults[$i]
echo "<td>" . utf8_encode($ldapResults[$i]['sn'][0]) . "</td>"
If you want to keep data attributes for your data source, you can do something like this
function format ( dataSource ) {
var html = '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">';
for (var key in dataSource){
html += '<tr>'+
'<td>' + key +'</td>'+
'<td>' + dataSource[key] +'</td>'+
'</tr>';
}
return html += '</table>';
}
$(function () {
var table = $('#example').DataTable({});
// Add event listener for opening and closing details
$('#example').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({
'Key 1' : tr.data('key-1'),
'Key 2' : tr.data('key-2')
})).show();
tr.addClass('shown');
}
});
});
td.details-control {
background: url('http://www.datatables.net/examples/resources/details_open.png') no-repeat center center;
cursor: pointer;
}
tr.shown td.details-control {
background: url('http://www.datatables.net/examples/resources/details_close.png') no-repeat center center;
}
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://cdn.datatables.net/responsive/1.0.1/js/dataTables.responsive.min.js"></script>
<script src="http://cdn.datatables.net/1.10.2/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" href="http://cdn.datatables.net/1.10.2/css/jquery.dataTables.css" />
<table id="example" class="display nowrap" cellspacing="0" width="100%">
<thead>
<tr>
<th></th>
<th>Item 1</th>
<th>Item 2</th>
<th>Item 3</th>
<th>Item 4</th>
</tr>
</thead>
<tbody>
<tr data-key-1="Value 1" data-key-2="Value 2">
<td class="details-control"></td>
<td>data 1a</td>
<td>data 1b</td>
<td>data 1c</td>
<td>data 1d</td>
</tr>
<tr data-key-1="Value 1" data-key-2="Value 2">
<td class="details-control"></td>
<td>data 2a</td>
<td>data 2b</td>
<td>data 2c</td>
<td>data 2d</td>
</tr>
</tbody>
</table>
You can create an array of the data you need to show for each row
EG :
var data = [
{ key1 :'value1', key2 :'value2', key3 :'value3'}, //Row1
{ key1 :'value1', key2 :'value2'} //Row2
];
And updated the format()
function format (index ) {
var json_data = data[parseInt(index)];
var op = '';
$.each(json_data, function(key, value){
op +='<div>' + key + ': '+ value + '</div>';
});
return op;
}
Now just add the index of the array in a custom attribute <tr data-child-index="1">
And finally row.child(format(tr.data('child-index'))).show();
EDIT : No html changes needed.
Calculate the index dynamically using jQuery index()
row.child(format($('#example td.details-control').index($(this)))).show();
DEMO
If are are getting json data to show as child then try it like,
else {
// Open this row
// pass your json data to show in details
row.child( format(myJsonData)).show();
tr.addClass('shown');
}
And in the format function change it like,
function format ( json ) {
var $json=$.parseJSON(json);// if not parsed
var str='';
$json.each(function(key,value){
str += '<div>'+key+':'+value+'</div>';
});
return str;
}

Javascript function for adding row to html table does not work

I developed two javascript functions which create a html table (showList4() ) and which add rows (addRow() ), pls see code below. showList4() works fine, but addRow() does not; addRow() adds row, but:
(error 1): the row is not added straight under the previous row, but shifted to the left.
(error 2): executing addRow the second time does not put the row after/under the previously added row, but before/above it.
For adding a row I looked at the solution at Add table row in jQuery but I dont know where I go wrong with the addRow() code?
javascript code:
function showList4(title, wrapper, wrappermethod, tableid){ //this functions works fine
if((!$('#'+wrapper).length) ){//no action if no info or invalid wrapper agurment type
return;
}//if
var form = '<table id="'+tableid+'">';
//add title
if(typeof title === 'string'){
form += '<caption> '+ title +' </caption>';
}//if
form += '<tbody>';
var nrofrows = arguments.length - 4;
console.log('Showlist3- nrofrows: '+nrofrows)
//add following rows with vert labels in first colum and datavalues in following columns
for(var row=0;row<nrofrows;row++){ //for each following row
form += '<tr> <td> ';
for(var column=0, column_max=arguments[4+row].length;column<column_max;column++){
if(arguments[4+row] !== undefined){
form += '<td>' + arguments[4+row][column] + ' </td>';
}//if
}//for(var column=0, column_max=labels_hori.length;column<column_max;column++){
form += '</tr>';
}//for(var i=0,i_max=labels_hori.length;i<i_max;i++
form += '<tr><td> </tr></td> </tbody> </table>'; //add empty line and finish table
switch(wrappermethod){
case 'html':
$('#'+wrapper).html(form);
break;
default: //no action if invalid wrapper argument
break;
};//switch
return;
};//showList4()
function addRow(tableid,values){
var form = '<tr>';
for(var i=0,i_max=values.length;i<i_max;i++){
form += '<td>' + values[i] + '</td>';
}//for
form += '</tr>';
$('#'+tableid+' > tbody:last').after(form); //reference example code: https://stackoverflow.com/questions/171027/add-table-row-in-jquery
//$('#'+tableid+' tr:last').after(form); //reference example code: https://stackoverflow.com/questions/171027/add-table-row-in-jquery
return;
}//addrow
$(document).ready(function(){
showList4('Title table','myDivid','html','myTable',
['Some text1','Some text2','Some text3','Some text4'],
['1','2','3','4']);
addRow('myTable',['A','B','C','D']);
addRow('myTable',['E','F','G','H']);
});//$(document).ready(function(){
html code:
<div id="myDivid" style="width:500px; "> </div>
There are a few things wrong with your code.
First, you are targeting the "last body tag" rather than the "last row in the body". So change your line
$('#'+tableid+' > tbody:last').after(form);
to
$('#'+tableid+' > tbody > tr:last').after(form);
This will ensure that the new row is inserted within the body of the table.
Second, you are creating an additional cell in your first two rows. See this line
form += '<tr> <td> ';
Change it to
form += '<tr>';
Third, you have broken html on this line
form += '<tr><td> </tr></td> </tbody> </table>'; //add empty line and finish table
Change it to
form += '<tr><td colspan="4"></td></tr></tbody> </table>'; //add empty line and finish table. NOTE the colspan to format the table correctly.
Here is a working version at jsbin
p.s. jsbin suggest some other fixes to missing semi-colons, misuse of the switch statement and unnecessary semi-colons.

Is there any way to handle a particular column while adding the rows dynamically?

I am generating a dynamic table on click of a button as below -
$('.addRowButton').click(function () {
++counter;
var index=counter-1;
var newRowHtml =
'<tr>' +
'<td>' + counter +
'</td>' +
'<td><input name="b2bProductList[' + index+ '].productId" class="variant b2bTableInput" /></td>' +
'<td align="center"><span id="pvDetails" class="pvDetails"></span></td>' +
'<td><div class="img48" style="vertical-align: top;"><img src=""></td>'+
'<td><input name="b2bProductList[' + index + '].quantity" class="qty b2bTableInput"/></td>' +
'<td align="center"><span id="mrp" class="mrp"/></td>' +
'<td align="center"><input id="totalPrice" readonly="readonly" class="totalPrice b2bTableInput" type="text"></td>' +
'</tr>';
$('#poTable').append(newRowHtml);
But I want to handle a particular column my self - kind of override it. I have to display an image and have to use some attributes in this column it which I can not put in the above code. How should I override it. If I am going to declare any tr or td in my table in the main <table></table> they are taking up extra row statically. Is there any particular way to handle a particular column while adding the rows dynamically?
EDIT - I have to set the source of the image, the source string of which I am fetching through an async call on the focusout of my Id textbox. Now can not set the source in the row generation time, so I will have to handle each column at a time giving src after I have fetched it. The column is mentioned in my code
'<td><div class="img48" style="vertical-align: top;"><img src=""></td>'+
Now I have to set the src. I hope this tells my problem clearly.
You could use
$('#myTable tr td::nth-child('+columnIndex+')')
if you know the index of the column or you could give that specific td a unique class
newRowHtml ='<tr>' + '<td class="someClass"></td> ... etc
and select that class:
$('.someClass').each(function(){
// Some other code
});
Alternatively you could add the rows like so:
var $row = $('<tr></tr>');
var $id = $('<td></td>').html(id);
var $someOtherfield = $('<td></td>').html(someOtherData);
$('#myTable').append($row.append($id).append($someOtherfield));
then use the variable $someOtherfield to access it and work on it.
$someOtherfield.find('img').attr('src' , yourSource);
The simplest to select the cells of a specific column, if you have no fancy colspan, is to use :nth-child() :
$('#mytable td:nth-child('+columnIndex+')')
var tds= $('#poTable').find("td:first");
will give you the first column.
and to iterate over the elements
tds.each(function(index){
//do your stuff here..
});
hope this helps..

Categories

Resources