DataTables with different number of columns - javascript

I am loading data using ajax and generating column names dynamically in my DataTable. My DataTable has different number of columns, depending on the selection by user.(There is a drop-down list).
For example, there are 2 options in drop-down list as Southern Province and Northern Province. Southern Province table has 4 columns and Northern Province table has 6 columns.
Scenario 1
First user select Southern Province which has 4 columns. Then it generates table without no errors, But after that if user select Northern Province which has 6 columns, table not generate and js console print error as below.
Uncaught TypeError: Cannot read property 'style' of undefined jquery.dataTables.js:3828
Scenario 2
First user select Northern Province which has 6 columns. Then it generates table without no errors, But after that if user select Southern Province which has 4 columns, table not generate and js console print error as below.
Uncaught TypeError: Cannot read property 'mData' of undefined jquery.dataTables.js:6122
But if both table has same number of columns, both tables generate without errors.
How can I solve this ?
Here is the JS Code
jQuery(document)
.ready(
function() {
$('#province-list').change(
function() {
var prov = $(this).val();
if (prov == "sp") {
make_SP();
} else if (prov == "np") {
make_NP();
}
});
function make_SP() {
$("#dataTables-res_item")
.dataTable(
{
"bDestroy" : true,
"bProcessing" : false,
"bServerSide" : true,
"sAjaxSource" : "/province_list_view?p_name=sp",
"aoColumns" : [
{
"mData" : "result_date",
"sTitle" : "Result Date"
},
{
"mData" : "result_day",
"sTitle" : "Result Day"
},
{
"mData" : "draw_number",
"sTitle" : "Draw Number"
},
{
"mData" : "draw_time",
"sTitle" : "Draw Time"
} ],
"order" : [ [ 0, "desc" ] ]
});
};
function make_NP() {
$("#dataTables-res_item")
.dataTable(
{
"bDestroy" : true,
"bProcessing" : false,
"bServerSide" : true,
"sAjaxSource" : "/province_list_view?p_name=np",
"aoColumns" : [
{
"mData" : "result_date",
"sTitle" : "Result Date"
},
{
"mData" : "result_day",
"sTitle" : "Result Day"
},
{
"mData" : "draw_number",
"sTitle" : "Draw Number"
},
{
"mData" : "draw_time",
"sTitle" : "Draw Time"
},
{
"mData" : "draw_place",
"sTitle" : "Draw Place"
},
{
"mData" : "draw_person",
"sTitle" : "Agent"
} ],
"order" : [ [ 0, "desc" ] ]
});
};
});

I faced the same issue when my updated data had different number of columns than the previous data. The recipe is really simple! In the scenario when there is a change in number of columns, Destroy function works in conjunction with $("#datatable").empty();. So before reloading data your code would contain following lines:
if (dataTableObject) { // Check if DataTable has been previously created and therefore needs to be flushed
dataTableObject.fnDestroy(); // destroy the dataTableObject
// For new version use table.destroy();
$('#' + DataTableDivID).empty(); // Empty the DOM element which contained DataTable
// The line above is needed if number of columns change in the Data
}
// DataTable data loading/reloading codes comes here
So overall, your code may look like this:
if(dataTableObject) { // Check if table object exists and needs to be flushed
dataTableObject.fnDestroy(); // For new version use table.destroy();
$('#myTable').empty(); // empty in case the columns change
}
var data = (province=='sp') ? sp : np;
var columns = (province=='sp') ? spColumns : npColumns;
dataTableObject = $('#myTable').DataTable({
columns: columns,
data: data
});

I think the safest way is to remove the table completely, and then re-insert it to the DOM before reinitialising. Seems to me that dataTables not completely removes all generated content, thats why the error(s) occurs (for different reasons). In theory it should work as above, more or less, but it doesn't. Consider this solution :
[full source in the demo link below]
var dataTable,
domTable,
htmlTable = '<table id="example"><tbody></tbody></table>';
function initDataTable(province) {
if ($.fn.DataTable.fnIsDataTable(domTable)) {
dataTable.fnDestroy(true);
$('body').append(htmlTable);
}
var data = (province=='sp') ? sp : np;
var columns = (province=='sp') ? spColumns : npColumns;
dataTable = $("#example").dataTable({
aaData : data,
aoColumns : columns
/* other options here */
});
domTable = document.getElementById('example');
}
$('#province-list').change(function() {
var prov = $(this).val();
initDataTable(prov);
});
This works. See demo -> http://jsfiddle.net/gss4a17t/
Basically it is the same as in OP, but instead of having different functions for different provinces, I have made different aoColumns for different provinces and so on. And instead of relying on bDestroy, I remove the entire <table> with dataTable.fnDestroy(true) (both DOM and and dataTables injections) and then reinserts the <table>-skeleton before reinitialising the dataTable.
I dont know if that is adaptable to OP's need, but this is how I would do it. It is more flexible for future changes, and the aoColumns-objects can be autogenerated from a script or achieved from the server by AJAX (if you want to have different titles for different languages, for example). "Belt and braces" :)

There is good solution up there but after ran into this question, dynamism still ring the bell on my mind. i would like to share this , not a export in js. So please comment.
function genericAjaxCall(url, tableId, _header, _dataMapping, isData,
isEditDelete) {
if (!isData) {
$.ajax({
url : url,
method : "GET",
dataType : "JSON",
success : function(data) {
var editDeletUrl = url.split("/");
var dataArray = createArray(_header, data, _dataMapping, url,
isEditDelete)
createListHeading(tableId, dataArray, false);
initDT(tableId, dataArray);
},
error : function(xhr) {
console.log(xhr);
openErrorModal("Guru", xhr.responseText);
}
});
} else {
var dataArray = createArray(_header, url, _dataMapping);
console.log(dataArray);
var finalData = dataArray + objName;
console.log(finalData);
createListHeading(tableId, dataArray, false);
initDT(tableId, dataArray);
}
}
function createArrayWithDelete(_header, data, _dataMapping, url) {
var posts = {};
posts.postDT = []
for (var i = 0; i < data.length; i++) {
var jsonData = {};
for (var j = 0; j < _header.length; j++) {
if (_dataMapping[j].indexOf('.') !== -1) {
var parts = _dataMapping[j].split(".");
if (String(data[i][parts[0]][parts[1]]).indexOf('*') !== -1) {
jsonData[_header[j]] = data[i][parts[0]][parts[1]].bold()
.replace("*", "");
} else {
jsonData[_header[j]] = data[i][parts[0]][parts[1]];
}
} else {
if (String(data[i][_dataMapping[j]]).indexOf('*') !== -1) {
jsonData[_header[j]] = data[i][_dataMapping[j]].bold()
.replace("*", "");
} else {
jsonData[_header[j]] = data[i][_dataMapping[j]];
}
}
}
if (_header[_header.length - 1]) {
jsonData["Action"] = deleteOnly(url,
data[i][_dataMapping[_dataMapping.length - 1]]);
}
posts.postDT.push(jsonData);
}
return posts.postDT;
}
function createListHeading(tableId, data, isAction) {
var posts = {
post : []
};
$.each(data[0], function(key, value) {
posts.post.push({
"mDataProp" : key,
"sTitle" : key
/* "sType" : "string" */
});
});
cols = posts.post
}
function initDT(tableId, results) {
// Construct the measurement table
data_table = $('#' + tableId).DataTable({
"iDisplayLength" : 10,
scrollCollapse : true,
"aaSorting" : [],
"aaData" : results,
"aoColumns" : cols
});
$('#' + tableId).DataTable().columns.adjust();
}
And this is how i call ,
$(function() {
var header = [ "H1", "H2", "H3", "H4" ];
var dataMapping = [ "d1", "d2", "d3", "d3" ];
genericAjaxCall("ajaxurlWhichreturnJSON", "tableId", header, dataMapping,
false, true);
});
Here d1, d2... are the key of your ajax response. Now we do not need to worry that which value user select. Note : this is not direct solution of this question but it is figurative

I know this is an old question, but since I spent several hours trying to solve it with DataTables 1.10.18, I publish it with the hope that it will help someone. I used JavaScript arrays as data source, and jQuery 1.12.4. It is based on #davidkonrad's answer.
HTML:
select province
<select id="province-list">
<option value="sp">sp</option>
<option value="np">np</option>
</select>
<br><br>
<div id="table-container">
<table id="example" class="display"></table>
</div>
JS:
var sp = [
[ 'col1', 'col2'] ,
[ 'col11', 'col22']
];
var np = [
[ 'col1', 'col2', 'col3', 'col4' ],
[ 'col11', 'col22', 'col33', 'col44' ]
];
var spColumns = [
{ title : "column1" },
{ title : "column2" }
];
var npColumns = [
{ title : "column1" },
{ title : "column2" },
{ title : "column3" },
{ title : "column4" }
];
var dataTable,
htmlTable = '<table id="example" class="display"></table>';
function initDataTable(province) {
if ($.fn.DataTable.isDataTable('#example')) {
dataTable = $('#example').DataTable();
dataTable.destroy(true);
$('#table-container').empty();
$('#table-container').append(htmlTable);
}
var data = (province=='sp') ? sp : np;
var columns = (province=='sp') ? spColumns : npColumns;
dataTable = $('#example').DataTable( {
"data": data,
"columns": columns
} );
}
$(document).ready(function() {
initDataTable('sp');
$('#province-list').change(function() {
var prov = $(this).val();
initDataTable(prov);
});
});

Related

Firebase: sort alphabetically a nested field in last children

I have this Firebase database with the following data:
Here is a part of the JSON file:
"SWPL6X" : {
"-LznFoTKvdc5318O97h_" : {
"direction" : "in",
"excluded" : false,
"in" : 422,
"multiplier_out" : 1.05,
"name" : "MaM.OUT.90F8",
"out" : 6582,
"total" : -6160
},
"-M2ZQU2azNc-JTKKXojU" : {
"direction" : "in",
"excluded" : false,
"in" : 316,
"multiplier_out" : 1.05,
"name" : "CdG.Out.96A4",
"out" : 2509,
"total" : -2193
},
"-M3zVSWzqb4TaezY1Kua" : {
"direction" : "in",
"excluded" : false,
"in" : 4806,
"name" : "MaM.in.3FBC",
"out" : 748,
"total" : 4058
},
I just need to order alphabetically the last children by the field "name" (String) but I can't find a way to achieve it.
The code before I tried to sort is the following:
getSensors: function(uid, event_id, success){
var ref=firebase.database().ref('clients_events_sensors/'+ uid+ '/'+ event_id);
kap.firebase.bindings.liveObject(ref, success, function(e){
kap.log('cant get client event sensors', e)
});
},
I tried changing the first line to this with no effect:
var ref=firebase.database().ref('clients_events_sensors/'+ uid+ '/'+ event_id).orderByChild('name');
This function is only called once here, the logs retrieved keep the same order as the one coming initially from Firebase:
app.data.getSensors(kap.page.uid, kap.page.event_id, function(sensors) {
$.each(sensors, function(k, sensor){
//remove picture to avoid blowing localstorage memory
sensor.photo=null;
//Replace total by in+out/2 in the app only
sensor.total = Math.round((sensor.in+sensor.out) / 2);
sensor.sum = sensor.in+sensor.out;
});
kap.log('got sensors', sensors);
kap.page.sensors = sensors;
kap.page.fill();
});
The log:
got sensors {
"-LznFoTKvdc5318O97h_":{"direction":"in","excluded":false,"in":492,"multiplier_out":1.05,"name":"MaM.OUT.90F8","out":6983,"total":3738,"photo":null,"sum":7475},
"-M2ZQU2azNc-JTKKXojU":{"direction":"in","excluded":false,"in":339,"multiplier_out":1.05,"name":"CdG.Out.96A4","out":2686,"total":1513,"photo":null,"sum":3025},
"-M3zVSWzqb4TaezY1Kua":{"direction":"in","excluded":false,"in":5150,"name":"MaM.in.3FBC","out":836,"total":2993,"photo":null,"sum":5986},
"-M9Y7Ed7JtPSKTUQXRhd":{"direction":"in","excluded":false,"in":470,"multiplier_out":1.05,"name":"Volt.Out.99CE","out":2323,"total":1397,"photo":null,"sum":2793},
"-MAa2FZ5z5fMejpSL7zp":{"direction":"in","excluded":false,"in":4615,"multiplier_in":1,"name":"CdG.In_A011","out":486,"total":2551,"photo":null,"sum":5101},
"-MAa2QX5rvjWlT_2z7YQ":{"direction":"in","excluded":false,"in":3729,"name":"Volt.In_A013","out":1203,"total":2466,"photo":null,"sum":4932},
"-MAa2UaqVDyxQrQojFiR":{"direction":"in","excluded":false,"in":400,"multiplier_out":1.05,"name":"Pagnol.Out_A014","out":2205,"total":1303,"photo":null,"sum":2605},
"-MBiKvKAAWQkU8RABWR0":{"direction":"in","excluded":false,"in":2613,"name":"Pagnol.In_A015","out":987,"total":1800,"photo":null,"sum":3600}}
Am I missing something?

Render TreeView in React using Material UI

I need to create a Tree View using Material UI React JS. But the catch is that the JSON in tree view has pre-defined structure. To view the structure please follow the link
https://codesandbox.io/s/1slq1
And, I am getting the JSON from an API in completely different format with array of Objects & more. Below is the example of JSON response from API
[{
"order":123,
"date" : 24-2-2020,
"item":{
"itemName" : "Item 01",
"price" : 120,
"charges" :[
{
"charge01" : "Delivery",
"amount" : 110
},
{
"charge02" : "Taxes",
"amount" : 10
}
],
"otherDetails" : [
{
"data" : "somedata"
}
]
}
}]
I need to create a function where above JSON is converted to pre-defined Material UI TreeView JSON. Below is the code what I have tried but no success :
const renderTreeData = (dataItem: any) => {
if (typeof (dataItem) == "object") {
for (var key in dataItem) {
return {
id:key,
name: dataItem[key].toString(),
children: renderTreeData(dataItem[key])
}
}
}
return renderData;
}
Below is the screenshot of what is required:

Fetch all data from json and display it on the index page

I have the following json:
[
{
"countryId" : 0,
"countryImg" : "../img/france.jpg",
"countryName" : "France",
"countryInfo" : {
"headOfState" : "Francois Hollande",
"capital" : "Paris",
"population" : 66660000,
"area" : 643801,
"language" : "French"
},
"largestCities" : [
{"Paris" : "../img/paris.jpg"},
{"Marseille" : "../img/marseille.jpg"},
{"Lyon" : "../img/lyon.jpg"}
]
},
{
"countryId" : 1,
"countryImg" : "../img/germany.jpg",
"countryName" : "Germany",
"countryInfo" : {
"headOfState" : "Angela Merkel",
"capital" : "Berlin",
"population" : 81459000,
"area" : 357168,
"language" : "German"
},
"largestCities" : [
{"Berlin" : "../img/berlin.jpg"},
{"Munich" : "../img/munich.jpg"},
{"Hamburg" : "../img/hamburg.jpg"}
]
}
]
I need put it in my index.html, however I don't understand why I get only the second object? I need to put two objects in index. Maybe I need to use a loop? How do I do this properly?
I have the following javascript code:
$.ajax({
method: "POST",
url: "../data/data.json"
}).done(function (data) {
/*console.log(data);*/
localStorage.setItem('jsonData', JSON.stringify(data));
var dataFromLocStor = localStorage.getItem('jsonData');
var dataObject = JSON.parse(dataFromLocStor);
console.log(dataObject);
function Countries(){
this.getCountries = function () {
var ulListElem = document.getElementById("list-of-teams").children,
imgCountry = document.createElement('img');
for(country in dataObject){
/*console.log(dataObject[team]['teamName']);*/
imgCountry.setAttribute('src', dataObject[country]['countryImg']);
imgCountry.setAttribute("width", "400");
imgCountry.setAttribute("height", "300");
console.log(country);
ulListElem[0].innerHTML = dataObject[country]['countryId'];
ulListElem[1].innerHTML = dataObject[country]['countryName'];
ulListElem[2].appendChild(imgCountry);
ulListElem[3].innerHTML = dataObject[country]['countryInfo']['headOfState'];
ulListElem[4].innerHTML = dataObject[country]['countryInfo']['capital'];
ulListElem[5].innerHTML = dataObject[country]['countryInfo']['population'];
ulListElem[6].innerHTML = dataObject[country]['countryInfo']['area'];
ulListElem[7].innerHTML = dataObject[country]['countryInfo']['language'];
}
}
}
var countriesDate = new Countries();
countriesDate.getCountries();
});
You are setting the same UI elements (img and ul) twice inside a loop. When the loop runs first time, the values are set from the first array element. When the loop runs second time, the SAME elements are overwritten with the new values.
To properly display all elemts from the JSON array, you need two sets of UI elements in the index.html page e.g. two imgs, two uls etc.

Dynamically creating nodes in a jstree tree structure

I have to retrieve a list of menu item from a database and display it in a tree structure I want to use the menu name as the node name and menu id as the id of the node.
The method I used was to retrieve the data using an ajax call and put them into a list and then display it as a tree.But I think dynamically creating nodes depending on the data is more efficient.
function createNodeList(){
$('#menuCPanel #contentData #tree').jstree({
"json_data" : {
/*"data" : [{
"data" : {title : "menuName"},
"attr" : {id : "menuId"},
"state" : "closed"
}
],*/
"ajax" :{
"type" : "POST",
"url" : "?m=admin?action=getNodeList",
"dataType" : "json",
"data" : function(result){
return {
id : result.attr ? result.attr("id") : result['menuId'],
title : result.attr ? result.attr("title") : result['menuName']
};
},
},
},
"callback" : {
},
"themes" : {
"theme" : "classic",
"dots" : true,
"icons" : true
},
"plugins" : ["json_data", "themes"]
}).bind("select_node.jstree", function (e, data) { alert(jQuery.data(data.rslt.obj[0], "jstree").id) });
}
}
this is the stucture of my json data
"data":[{"menuId":"1","menuName":"Top Menu"},{"menuId":"2","menuName":"Main Menu"},{"menuId":"3","menuName":"Bottom Menu"},{"menuId":"4","menuName":"Main Menu"}]}
I would like to know what is wrong with the above result and how to dynamically create a node within in the ajax.success();
I went through some examples but all of them use the jstree.cretate() which i can't invoke inside jstree.json_data.ajax.success()
thanks in advance :)
This is a standard jstree with json data, which also binds select_node to do actions when a node is selected. Nodes must not have an ID which are plain numbers or contain jquery special selector characters. Number IDs must have a standard character first. so 1 should be N1, 2 should be N2 for example.
$('#MyTreeDiv').jstree({
"json_data": {
"ajax": {
"type": "POST",
"url": "/MyServerPage.aspx?Action=GetNodes",
"data": function (n) { return { id: n.attr ? n.attr("id") : 0} },
}
},
"themes": {
"theme": "default",
"url": "/Content/Styles/JSTree.css",
"dots": false
},
"plugins": ["themes", "json_data", "ui", "crrm"]
}).bind("select_node.jstree", function (e, data) {
var selectedObj = data.rslt.obj;
alert(selectedObj.attr("id"));
});
The json returned from your server must be in the correct format as defined in the jstree documentation, and must no include several special characters, unless those characters are escaped or the json created using serialization.

Read json data using getjson

I have a json file:
{
"bible" : {
"#attributes" : {
"translation" : "ASV"
},
"testament" : [
{
"#attributes" : {
"name" : "Old"
},
"book" : [
{
"#attributes" : {
"name" : "Genesis"
}
},
{
"#attributes" : {
"name" : "Exodus"
}
},
{
"#attributes" : {
"name" : "Leviticus"
}
},
{
"#attributes" : {
"name" : "Numbers"
}
},
{
"#attributes" : {
"name" : "Deuteronomy"
}
},
{
"#attributes" : {
"name" : "Joshua"
}
},
{
"#attributes" : {
"name" : "Judges"
}
},
{
"#attributes" : {
"name" : "Ruth"
}
}
]
}
]
}
}
I am using code to read it:
$(document).ready(function(){
$.getJSON("asv/index.json", function(json) {
alert("JSON Data: " + json.bible.testament[1].name);
});
});
But this gives me undefined. Please let me know how to read book names. Also #attributes are what for?
Thanks
try this:
$.getJSON('asv/index.json',
function(json) {
$.each(json.bible.testament[0].book, // $.each() looping on each books
function(i, value) {
console.log(value['#attributes'].name); // here you get the name of books
});
json.bible.testament[1].name is undefined.
try json.bible.testament[1]['#attributes'].name
You have the wrong object path to your data. I recommend that you paste your json data into a viewer to make it easier to see what you need to get. Try http://jsonviewer.stack.hu/ for example.
<script type="text/javascript">
$(document).ready(function(){
$.getJSON("asv/index.json", function(json) {
alert(json.bible.testament[0]['#attributes'].name);
alert(json.bible.testament[0].book[0]['#attributes'].name);
});
});
</script>
That works for me. Notice how you don't have any testament[1] index, only testament[0].
The #attributes part seems to be something the script that generates the JSON is creating, nothing you need to use JSON per say. I would remove it if I had access to the JSON-creating script, but perhaps it is used in some system that you do not see.
If you have a browser that supports console.log (Firefox for example) you can do a 'console.log(json)' and look at the structure.
You can access names like that:
json.bible.testament[0].book[0]['#attributes'].name
json.bible.testament[0].book[1]['#attributes'].name
...

Categories

Resources