How to draw a table with dynamic data using D3 and DataTable - javascript

I am trying to draw a table using D3 and also using DataTable() for other functionalities like sorting the columns, overall table search etc. But I am not able to go along with DataTable. I have dynamic data coming which I will have to populate in the table dynamically(extra rows addition). For this, I delete the entire table everytime I am tabulating the table. I am able to do it with normal d3 table, but when I try to use the DataTable, it throws the below error.
I have a drop-down menu from which user can select any item. listen_change listens for such a change and draws the table with data corresponding to that item which I fetch using ajax from DB. DB keeps getting updated from somewhere else.
I want to know a way to use DataTable without reinitialising it (as the error alert tells) for my dynamic table. Any help would be appreciated. Thanks in advance.
I am getting the following alert,
DataTables warning: table id=table_id - Cannot reinitialise
DataTable. For more information about this error, please see
http://datatables.net/tn/3
The Javascript part of the code is:
function listen_change() {
var timeOutId = 0;
document.getElementById('select_item').addEventListener('change',function() {
var e = document.getElementById("select_item");
var item_name = e.options[e.selectedIndex].text;
clearTimeout(timeOutId);
(function worker() {
$.ajax({
url: '/my_url/' + item_name + '/',
success: function(data) {
//delete_table(table_id)
table_data(data)
},
complete: function() {
// Schedule the next request when the current one's complete
timeOutId = setTimeout(worker, 3000);
}
});
})();
});
}
function table_data(data){
//puts data in the format required to draw D3 table
tabulate(table_id, data, columns)
}
function tabulate(container, data, columns){
var table = d3.select(container).attr("width",1)
var thead = table.append('thead')
var tbody= table.append('tbody')
var headers = thead.append("tr")
.selectAll("th")
.data(columns)
.enter()
.append("th")
.text(function(column) {
return column;
})
// create a row for each object in the data
var rows = tbody.selectAll("tr")
.data(data)
.enter()
.append("tr");
// create a cell in each row for each column
var cells = rows.selectAll("td")
.data(function(row) {
return columns.map(function(column,id) {
return {column: column, value: row[column]};
});
})
.enter()
.append("td")
.attr("style", "font-family: Arial") // sets the font style
.html(function(d) {
return d.value;
});
$(document).ready(function(){
var o_table = $('#table_id').DataTable({
'bSort': true,
'bFilter': true,
"autoWidth":true,
"scrollY": "600px",
"scrollCollapse": true,
"info": true,
"paging": true,
"bLengthChange" : true,
"iDisplayLength":60,
"aLengthMenu" : [30,60,100],
});
})
return table;
}
The html part is:
<div>
<table class="table table--bordered table--highlight" align='center' cellspacing=50 cellpadding=20 id="table_id">
</table>
</div>
<script type="text/javascript">
$(document).ready(function(){
listen_change();
});
</script>
UPDATE 1
I tried giving 'bDestroy' : true in DataTable()and I am clearing data inside the table using $('table_id').empty(). This helps me in destroying the DataTable instance and helps me replace the table and thus the data changes dynamically. But, this brings me to a new problem now. I am updating the table every 3 seconds as mentioned in the code. Now, if user tries to search for anything using the search option, it works but after 3 seconds, the whole table comes up back. How to update the data dymanaically without affecting the search feature? Any help would be appreciated. Thanks in advance.

Related

Change color of a cell after editing in datatable

I have a table in which I can edit and modify each cell.
I would like to highlight the cell that I modified.
At the moment I can only highlight the entire row but I don't have what I want to do.
I use createdRow to make the cells editable and get the modified row.
How can I do to highlight that modified cell?
var table = $("#deploymentMap_table").DataTable({
data: constructRaws(dataSet),//tbody
paging: false,
searching: false,
info: false,
fixedHeader: true,
scrollY: false,
scrollX: false,
responsive: false,
dom: 't', //display only the table
order: [[ 0, 'asc' ]],//order by 'service' col
columnDefs:[
{
targets:'_all',
render:function(data){
if(data == null) {return ""
} else {return data;}
}
},
{ targets: [0,1], "width" : "200px"},
],
columns: constructColumns(dataSet),//thead
dom: 'Bfrtip',
// attribute classname (background color) for services
rowCallback: function(row, data, index){
if ( data.code == 1 ) {
$('td', row).each( function ( value, index ) {
if($(this).contents().first().text()){
$(this).addClass('td_colorCD');
}
} );
}
$(row).find('td:eq(0)').css('background-color', '#7f7f7f').css('color', '#fff').css('text-align', 'left');
$(row).find('td:eq(1)').css('background-color', '#7f7f7f').css('color', '#fff').css('text-align', 'left');
$.each(row.childNodes, function(i,value){
if(value.innerText == "NoUP"){
$(value).addClass('td_colorBSF');
}
else if(value.innerText){
$(value).addClass('td_color');
}
})
},
// Make all cell editable
createdRow: function(row, data, dataIndex, cells) {
console.log(cells);
let original
row.setAttribute('contenteditable', true)
row.setAttribute('spellcheck', false)
row.addEventListener('focus', function(e) {
original = e.target.textContent
})
row.addEventListener('blur', function(e) {
if (original !== e.target.textContent) {
$('td', row).removeClass();
$('td', row).addClass('td_color_change');
const r = table.row(e.target.parentElement)
r.invalidate();
var lign = e.target.innerText;
lign = lign.split('\t');
var nRow = $('#deploymentMap_table thead tr')[0].innerText;
head = nRow.split('\n\t\n');
var newAR = mergeArrayObjects(head, lign);
console.log("newAR", newAR);
$(dataSet).each(function( index, values ) {
if(newAR.service[0].Services == values.service_name){
delete values.regions;
values.regions = newAR.region;
console.log(values);
}
})
console.log("dataset", dataSet);
}
})
}
});
I think the easiest way to handle this is to replace your rowCallback with a DataTables delegated event.
Below is a simple example which would change the color of a specific cell when you leave that cell:
Step 1) The onblur event requires the cell to have a tabindex attribute. You can add this however you wish - but here is one way, in your existing code:
$.each(row.childNodes, function(i,value){
$(value).attr('tabindex', i); // this line is new
// your existing code goes here
})
Note - this could be improved as it repeats tab indexes across rows. But it illustrates the approach.
Step 2: Add a new onblur event listener, after the end of your DataTable definition:
$('#deploymentMap_table td').on('blur', function () {
this.classList.remove("td_color");
this.classList.add("td_color_change");
} );
Step 3: The above code would need to be enhanced to include your edit-checking logic, which checks for an actual cell value change.
You can get the "before" cell values using this:
table.cell( this ).data();
And the "after" cell values using this - which gets the value from the HTML table (the DOM node), not from DataTables:
table.cell( this ).node().textContent;
The updated listener would be something like this:
$('#deploymentMap_table td').on('blur', function () {
var cellValueStart = table.cell( this ).data();
var cellValueEnd = table.cell( this ).node().textContent;
//console.log( cellValueStart );
//console.log( cellValueEnd );
if (cellValueEnd !== cellValueStart) {
table.cell( this ).data(cellValueEnd);
this.classList.remove("td_color");
this.classList.add("td_color_change");
}
} );
The table.cell( this ).data(cellValueEnd) command updates the cell in DataTables so that it matches the value you typed into the HTML cell. If you do not do this, then the data in the DataTables object (behind the scenes) will be out-of-sync with the data in the HTML table (what you see on your screen).
Warning: This approach is basic. It does not cover the case where a user may do the following:
Edit a cell from "A" to "B".
Leave the cell, so it is highlighted.
Return to the cell and edit it back from "B" to "A".
Leave the cell again.
In this case, the cell will remain highlighted.
One way around this is to capture the original state of every cell when you first load the table - and then check each edit against the value in the original data. This can be done, if needed - but is outside the scope of this question. But it also depends on what you need to do with the data, after you have finished editing it. If this is important to you, then it may be worth asking a new question for that specific problem.

DataTable JQuery How to remove a row from a table based on an ID?

I have a web application where you can drag and drop pictures into boxes. If you drop it in the one it will add the picture's information to the datatable, if you drop in the left it will remove the data from the datatable. I was wondering If there was a way I could remove the row based on the id?
$('#pictures')
.dataTable({
"columnDefs": [{
"orderable": false,
"targets": 1
},
{
"orderable": false,
"targets": 3
}
]
});
var t = $('#pictures')
.DataTable();
$("#left")
.droppable({
accept: ".draggable",
drop: function (event, ui) {
console.log("drop");
$(this)
.removeClass("border")
.removeClass("over");
var dropped = ui.draggable;
var droppedOn = $(this);
$(dropped)
.detach()
.css({
top: 0,
left: 0
})
.appendTo(droppedOn);
var $id = $(this)
.children()
.last()
.attr('id');
var rowId = pictures[id].id;
t.row(pictures[$id].id)
.remove()
.draw(false);
}
});
This obviously isn't the entire thing; however, I think this is enough to identify any problems.
You can use DataTable's API to remove the corresponding row:
t.row("your selector here").remove().draw();
in row() you could use several kind of selectors. If you saved the row's id in a variable, simply use
t.row("#"+rowId).remove().draw();
Note that you have to call draw() after a remove, since datatables doesn't redraw itself after a remove due to performance reasons.
$('#pictures').DataTable().row("#YourRowId").remove().draw();
If the item you want to delete is the parent window;
window.parent.$('#pictures').DataTable().row("#YourRowId").remove().draw();
If you want to delete all the rows;
$('#pictures').DataTable().row().remove().draw();
You should pay attention to the JS version.
var table1= $('#TableID').DataTable({
"createdRow": function (row, data, dataIndex) {
var rowID = "row_" + data[0];
$(row).attr('id', rowID);
}});
var rowID = "#row_" + any format that follows as per your row ID; table1.row(rowID).remove().draw();
This will work for everyone. Here, in "createdRow" callback of the DataTable constructor, you can define all the data based styling that you want on your DataTable. I Have defined a row ID to be assigned to each row based on the value of the first column.

DataTable does not re-draw after updating rows

I'm using the DataTables library to create a table with extra functionality. What I'd like to achieve, is that the user can select rows and then press a button. This will call the server, do some stuff and the rows should be updated accordingly.
However, after I'm done iterating over the rows to be changed and setting its values, re-drawing the table does not actually update its values. I update the data object, invalidate the cache and call table.draw(). It's very similar to the last example on this page.
I have created a JSFiddle of this issue. The button updates the date objects of the selected rows and the table is re-drawn, but the data inside the table is not updated. The core JS code:
$('#updateRow').click(function() {
//Get the table
var table = $('#example').DataTable();
//Iterate over selected rows
var rowData = table.rows({
selected: true
}).every(function() {
//For every selected row, update startDate
var d = this.data();
d.startDate = "01/01/2017";
console.log('Set startDate of ' + d.name + ' to ' + d.startDate);
//Invalidate the cache
this.invalidate();
});
//Re-draw the table
table.draw();
});
I forked and did the solution from your JsFiddle. Here's the relevant snippet from fiddle https://jsfiddle.net/k38r9be5/1/
var rowData = table.rows({
selected: true
}).every(function(rowIdx) {
var colIdx = 4; // startDate is the fifth column, or "4" from 0-base (0,1,2,3,4...)
table.cell( rowIdx, colIdx).data('01/01/2017').draw();
});
Basically, via the API you can get the cell object itself, and modify the contents with .data(). In your version you weren't actually getting a particular cell object and instead just copied the data contents of the row to a variable, and modified that.

How can I extract a selected row's data on Datatables

I have initialised a simple Datatable:
//initialise table
var dataTable = $('#example').DataTable({
searching: false,
responsive: true
});
//hide unnecessary columns
dataTable.columns(1).visible(false);
dataTable.columns(2).visible(false);
dataTable.columns(3).visible(false);
dataTable.columns(4).visible(false);
dataTable.columns(5).visible(false);
dataTable.columns(6).visible(false);
dataTable.columns(7).visible(false);
dataTable.columns(8).visible(false);
It can contain any number of records but I would like to take the values from all of the columns (only 1 is displayed to the user) and insert them into input fields (which may or may not be visible). I have successfully been able to select the rows using:
$('#example tbody').on( 'click', 'tr', function () {
if ( $(this).hasClass('selected') ) {
$(this).removeClass('selected');
}
else {
dataTable.$('tr.selected').removeClass('selected');
$(this).addClass('selected');
}
});
I have been looking into the Datatables API, row(), cells() etc and whilst I can view the data() method I simply can't see how to extract data from EACH cell on the row into the input text fields on the same webpage. I have also looked at fnGetSelectedData but I didn't get far as it always returned undefined via the console.
To explain the use case, it's essentially an Address Lookup. Each column in the table represents part of the address, I want to take the cells from the selected row and insert it into the form as a users selected address.
Any help is appreciated
SOLUTION
Use the code below to get data for the selected row:
var data = $('#example').DataTable().row('.selected').data();
Then you can populate your input fields as shown below:
$('#name').val(data[0]);
$('#email').val(data[1]);
See this jsFiddle for demonstration.
NOTES
You can simplify your initialization code:
var dataTable = $('#example').DataTable({
searching: false,
responsive: true
columnDefs: [
{
targets: [1,2,3,4,5,6,7,8],
visible: false
}
]
});
To get an object with the values use:
yourTableVariable.rows({ selected: true }).data();
Then you can do something like this to get specific value(example id):
yourTableVariable.rows({ selected: true }).data()[0].id;

jquery datatable whole column background color

I have a datatable, I want to color code the column based value in the last row.
If the TYPE value is "O" then apply yellow color, otherwise nothing. My columns are dynamic.
expected result:
var dt= $(element).dataTable({
deferRender: true,
destroy: true,
"aaData": data, // data is coming from service
"aoColumns": columns // column is dynamic
});
SOLUTION
You can use drawCallback to handle table draw event and enumerate columns data with columns().every() to find columns containing required values and highlight them.
var table = $('#example').DataTable({
drawCallback: function(){
var api = this.api();
api.columns().every( function () {
var data = this.data();
if($.inArray('O', data) !== -1){
$(this.nodes()).addClass('highlight');
} else {
$(this.nodes()).removeClass('highlight');
}
});
}
});
Please note that the code above detects O in all rows. To handle only the last row you need to add more code.
DEMO
See this jsFiddle for code and demonstration.
Use the rowCallback
$(element).dataTable({
"rowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
switch(aData[0]){
case 'O':
$(nRow).css('backgroundColor', 'yellow');
//also style other rows here
break;
}
}
});
Simple demo: Fiddle

Categories

Resources