Console log not waiting for function call to finish - javascript

I have a function that involves fetching data via ajax and making a table and returning said table. I also have a function that gets triggered when a button is clicked. I have names this function with async and hoped that when i called the function to get the table, it would wait for it to finish (I have used await) then the rest of the code would follow. But instead the console logs the table with no data in it (because it needs time for the data to be fetched), instead console logs the empty table right away.
The trigger function
const exportPdf = async () => { //Called from a button
let tableBody = await getTableBody('v7xn82Ff3XQFYwCl');
console.log(tableBody);
}
The function that returns the table
const getTableBody = async (crop) => {
table = $('#pests_js_table').DataTable({
"pageLength": 50,
"processing": true,
"ajax": {
"url": '/assets/ajax/table_ajax_handler.php',
"type": "POST",
"data": {
action: "getPestsForTable"
}
},
"rowsGroup": [
],
"columns": [{
"data": "crop"
},
{
"data": "pests"
},
{
"data": "chemical"
},
{
"data": "product"
},
{
"data": "rate"
},
{
"data": "max_no"
},
{
"data": "hi"
},
{
"data": "mrl"
},
{
"data": "pcs_no"
},
{
"data": "supplier"
},
{
"data": "use_by_date"
}
],
"columnDefs": [{
"targets": [0],
"visible": false,
"searchable": true
},
{
"targets": [1],
"visible": true,
"searchable": true
}
],
"order": [
[2, "asc"]
],
"rowsGroup": [
1, 2, 4, 5, 6, 7, 9
]
});
let tableBody = $('#pests_js_table').html();
table.destroy();
return tableBody;
}

Your getTableBody function returns a string immediately since it's not awaiting anything. So if the result of calling DataTable isn't awaitable you need to return a Promise and call resolve when you actually have a value to return, like:
const getTableBody = async crop => {
return new Promise((resolve, reject) => {
const table = $('#pests_js_table').DataTable({
ajax: {
success: resp => {
resolve($('#pests_js_table').html()) // <-- call resolve when ready
table.destroy()
},
... // etc.
},
... // etc.
})
})
}

Related

DataTable Range Datetime Filter

I've a datatable which i fill with ajax response. In the response i'm getting a string date data. I can write that data on datatable but i can't filter the data with date range. I've try so much way but i can't solve this. some of my trying i get " fnDraw()" is not function or some error like this. how can I make the range filter ?
JavaScrıpt code :
$(document).ready(function () {
var table = $.ajax({
type: "GET",
url: '/History/GetCallbackHistory',
data: { UserId: document.getElementById("callbackuserid").value },
dataType: 'json',
success: function (obj, textstatus) {
$('#callback_table').DataTable({
"pagingType": "input",
"language":
{
"processing": "<div class='loader'>Loading...</div>",
"paginate": {
"previous": "",
"next": ""
},
},
dom: "<'row'<'container-c'pi<'permuheader'<'refresh-button'>><'tlength'l>>>"
+ "<'row'>"
+ "<'row'<'col-sm-12't>r>",
data: obj,
columns: [
{
"data": "Id"
},
{
"data": "DateCallback"
},
{
"data": "callbackId"
},
{
"data": "Task_name"
},
{
"data": "callbackStatus"
},
{
"data": "Point"
},
{
"data": "TransactionType"
},
{
"data": "DateEnd"
}
]
});
},
error: function (obj, textstatus) {
alert(obj.msg);
}
});
$("#datepicker_from").datepicker({
showOn: "button",
buttonImageOnly: false,
"onSelect": function (date) {
minDateFilter = new Date(date).getTime();
table.fnDraw();
}
}).keyup(function () {
minDateFilter = new Date(this.value).getTime();
table.fnDraw();
});
$("#datepicker_to").datepicker({
showOn: "button",
buttonImageOnly: false,
"onSelect": function (date) {
maxDateFilter = new Date(date).getTime();
table.fnDraw();
}
}).keyup(function () {
maxDateFilter = new Date(this.value).getTime();
table.fnDraw();
});
});
$.fn.dataTableExt.afnFiltering.push(
function (oSettings, aData, iDataIndex) {
if (typeof aData._date == 'undefined') {
aData._date = new Date(aData[1]).getTime();
}
if (minDateFilter && !isNaN(minDateFilter)) {
if (aData._date < minDateFilter) {
return false;
}
}
if (maxDateFilter && !isNaN(maxDateFilter)) {
if (aData._date > maxDateFilter) {
return false;
}
}
return true;
}
);
When you use jQuery's ajax like this:
var table = $.ajax({ ... });
You are assigning the jQuery object to your table variable. You are not assigning the DataTable from the success function to the table variable.
This is why, when you try to use table.fnDraw(), you get that specific error: Your table is not a DataTable. The ajax call is asynchronous - it does not return anything from the success call in the normal flow of your code.
Instead, the simplest alternative that I would recommend is to re-arrange your code to use DataTables' built-in support for ajax.
In this new approach we do not need to use the jQuery ajax function at all - so we completely remove that from the code. Instead, we do this:
var table = $('#callback_table').DataTable({
"ajax": {
"method": "GET",
"url": "/History/GetCallbackHistory",
"data": {
UserId: document.getElementById("callbackuserid").value
},
"dataType": "json",
"dataSrc": ""
},
"pagingType": "input",
"language": {
"processing": "<div class='loader'>Loading...</div>",
"paginate": {
"previous": "",
"next": ""
},
},
"dom": "<'row'<'container-c'pi<'permuheader'<'refresh-button'>><'tlength'l>>>" +
"<'row'>" +
"<'row'<'col-sm-12't>r>",
"columns": [{
"data": "Id"
},
{
"data": "DateCallback"
},
{
"data": "callbackId"
},
{
"data": "Task_name"
},
{
"data": "callbackStatus"
},
{
"data": "Point"
},
{
"data": "TransactionType"
},
{
"data": "DateEnd"
}
]
});
The main point to note is the ajax section:
"ajax": {
"method": "GET",
"url": "/History/GetCallbackHistory",
"data": {
UserId: document.getElementById("callbackuserid").value
},
"dataType": "json",
"dataSrc": ""
},
This is a wrapper around the jQuery ajax function. But it also uses a DataTables extension to jQuery's ajax: the dataSrc option. This option replaces your old data: obj option. It tells DataTables that your JSON response is a plain array.
Once you have done this, then your table variable will contain a valid DataTables object - and you can now use table.fnDraw();. But it would be better to use the modern name for this function: table.draw();.
If you have filtering problems, after that, you can refer to the official date range filtering example DataTables date range filter, to make sure your approach matches the example's approach (but using your preferred datepicker controls).

Get body of datatable as HTML element

I have a datatable that is being populated with data from ajax->mysql database. After it is populated I use various datatables tools like "rowsGroup", "searchColumns" etc to make the table look better.
Is there any way I can then get the table body as a tbody element with td's and tr's and append it to a variable?
My problem is that I have the datatable looking as I want it when it is initialized in javsscript (with the filters and plugins etc applied) but I have no way of exporting it like that.
My question is, how can I export it to a variable looking exactly how it is so that I can save it somewhere and re-use it elsewhere on the page or project.
===TABLE INIT===
let table;
const getTableBody = async (crop) => {
table = $('#pests_js_table').DataTable({
"pageLength": 50,
"processing": true,
"ajax": {
"url": '/assets/ajax/table_ajax_handler.php',
"type": "POST",
"data": {
action: "getPestsForTable"
}
},
"rowsGroup": [
],
"columns": [{
"data": "crop"
},
{
"data": "weeds"
},
{
"data": "chemical"
},
{
"data": "product"
},
{
"data": "rate"
},
{
"data": "max_no"
},
{
"data": "hi"
},
{
"data": "mrl"
},
{
"data": "pcs_no"
},
{
"data": "supplier"
},
{
"data": "use_by_date"
}
],
"searchCols": [{
"search": String(crop) || null
}],
"columnDefs": [{
"targets": [0],
"visible": false,
"searchable": true
},
{
"targets": [1],
"visible": true,
"searchable": true
}
],
"order": [
[2, "asc"]
],
"rowsGroup": [
1, 2, 4, 5, 6, 7, 9
]
});
return table.outerHTML;
}
const exportPdf = async () => {
let crops = <?=json_encode($crops)?>;
//console.log(crops);
// crops.map(async crop => {
// let tableBody = await getTableBody(crop);
// console.log(tableBody);
// });
let tableBody = await getTableBody('v7xn82Ff3XQFYwCl');
console.log(tableBody);
}
For a more complex requirement like the one you have, you are going to need to combine DataTables capabilities with some extra logic (JavaScript, in this case) to iterate over the set of tables you need.
So, the following example is not a complete solution - it just shows how to create one copy of your original table, with an applied filter. But this could be extended to loop over each of your continents, one-by-one.
The code creates a variable called tableCopy which contains a clone of the original table. You can then use tableCopy.outerHTML to get the full HTML of the copied table.
$(document).ready(function() {
let table = $('#example').DataTable( {
// my test data is sourced from a JavaScript variable, not from ajax:
data: dataSet,
// my custom code will not work if deferRender is true:
"deferRender": false,
// for testing, provide pre-filtered data:
"search": {
"search": "ni"
},
columns: [
{ title: "ID", data: "id" },
{ title: "Name", data: "name" },
{ title: "Office", data: "office" },
{ title: "Position", data: "position" },
{ title: "Start date", data: "start_date" },
{ title: "Extn.", data: "extn" },
{ title: "Salary", data: "salary" }
]
} );
let tableCopy = document.getElementById("example").cloneNode(true);
tableCopy.id = 'my_copy'; // the cloned table needs a unique ID
//remove the (incomplete) set of <tr> nodes in the <tbody> node, since they
// only account for the initially displayed set of rows (one page of data):
$('tbody', tableCopy).empty();
// we will not select any rows which have been filtered out (and are therefore hidden):
let rowsSelector = { order: 'current', page: 'all', search: 'applied' };
// build a complete set of <tr> nodes using the DataTables API:
table.rows( rowsSelector ).every( function ( rowIdx, tableLoop, rowLoop ) {
$('tbody', tableCopy).append( this.node() );
} );
//console.log( tableCopy.outerHTML );
$('#displayTarget').html( tableCopy.outerHTML );
// redraw the main table to re-display the removed <tr> nodes:
table.draw( false );
} );
In your case, you would need to extend this approach to handle multiple copies of the table.
I have not tested the following - but this shows the approach:
You would need an array of values to hold the copies:
let tableCopies = [];
contients.forEach(function (item, index) {
tableCopies.push(document.getElementById("example").cloneNode(true));
tableCopies[index].id = 'table_' + item; // e.g.: 'table_Europe'
});
You would need to extend this section:
table.rows( rowsSelector ).every( function ( rowIdx, tableLoop, rowLoop ) {
$('tbody', tableCopy).append( this.node() );
} );
Instead of using the filtered data from the original table, you would loop through every row - and you would not use the rowsSelector:
table.rows().every( ... )
In the body of that function, you would check which continent is in the current row:
table.rows( rowsSelector ).every( function ( rowIdx, tableLoop, rowLoop ) {
let continent = this.data().continent;
// and add the relevant continent row to the related object in the `tableCopies` array.
} );

Wait for map function to finish but append to variable

Currently my code is like so:
const exportPdf = async () => {
let crops = <?=json_encode($crops)?>;
let pestTables;
let fillPestTables = crops.map(crop, index) => {
let tableBody = await getTableBody(crop, index);
$(pestTables).append(tableBody);
});
console.log(pestTables);
}
The function you see, gets called when a button is clicked. As of now, the console.log(pestTables) gets called straight away. The function being called in the map works fine as if I do a console.log in the map function, it logs what I want. Here is the function in the map:
const getTableBody = async (crop, index) => {
return new Promise((resolve, reject) => {
table = $(`#pests_js_table${index}`).DataTable({
"pageLength": 50,
"processing": true,
"ajax": {
"url": '/assets/ajax/table_ajax_handler.php',
"type": "POST",
"data": {
action: "getPestsForTable"
}
},
"rowsGroup": [
],
"columns": [{
"data": "crop"
},
{
"data": "pests"
},
{
"data": "chemical"
},
{
"data": "product"
},
{
"data": "rate"
},
{
"data": "max_no"
},
{
"data": "hi"
},
{
"data": "mrl"
},
{
"data": "pcs_no"
},
{
"data": "supplier"
},
{
"data": "use_by_date"
}
],
"columnDefs": [{
"targets": [0],
"visible": false,
"searchable": true
},
{
"targets": [1],
"visible": true,
"searchable": true
}
],
"order": [
[2, "asc"]
],
"rowsGroup": [
1, 2, 4, 5, 6, 7, 9
],
"initComplete": function() {
resolve($(`#pests_js_table${index}`).html());
table.destroy();
}
});
})
}
I am very new to using promises so if I have messed something up I apologize. Anyone that can guide me in the right direction or explain what needs to happen here?
Thanks
You need to use async function for the map to finish
let fillPestTables = crops.map(async function (crop, index) => {
let tableBody = await getTableBody(crop, index);
$(pestTables).append(tableBody);
});
To generate a awaitable from a list of promises use Promise.all():
const fillPestTables = await Promise.all(
crops.map(
async (crop, index)=>{/*...*/}
)
)
This executes all the promises at the same time and resumes when all of them have been completed, returning a list of the responses.
Here is the solution:
const exportPdf = async () => {
let crops = <?=json_encode($crops)?>;
let pestTables;
let fillPestTables = crops.map(crop, index) => {
await getTableBody(crop, index).then(res => $(pestTables).append(res););
});
console.log(pestTables);
}

How to multiply Data in Datatables?

How do I multiply Data in Datatables ?
I have Datatables and javascript that look like this:
$('#xxdata').DataTable( {
"destroy": true,
"processing": true,
"ajax": {
url : "xxreport.php",
type : 'GET',
data : {
datedari : SplitRange[0].trim(),
datesampai : SplitRange[1].trim()
}
},
"columns": [
{ "data": "offerName" },
{ "data": "offerCountry" },
{ "data": "visits" },
{ "data": "conversions" },
{ "data": "profit"}
]
} );
I want to multiply data in { "data": "profit"} maybe like
this { "data": "profit" * 0.7}
Can I change data in datatables as I want? Or can anyone give other solutions?
Thank You.
You can use the columns.render option (documented here) to do this.
"columns": [
{ "data": "offerName" },
{ "data": "offerCountry" },
{ "data": "visits" },
{ "data": "conversions" },
{ "data": "profit",
"render": function (data) {
return data * 0.7;
}
}
]
In this case, data in the function signature represents the data for the cell. There are other options that can be passed into the function, but in your case these do not need to be included since this is such a simple operation. See the documentation link if you ever want to expand to a more complicated rendering function
You must add a render to your column, something like this:
{ "data": "profit", "render": renderMyProfit}
and you should have the rendering function declared before you call the .DataTable() function.
var renderMyProfit = function (data, type, row, meta) {
var renderContent = "<div>*</div>";
return renderContent.replace("*", row.profit * 0.7);
};

loading data in datatable on onchange event

I want to implement function in which data will be loaded into datatable after onChange event. So for that I am trying to implement code as below.
var viewdatatab = $('#dataTablesFeedback').dataTable({
"columns": [
{ "data": "resourceId" },
{ "data": "feedbackRecommendation" },
{ "data": "technicalSkillGaps" },
{ "data": "technicalAvgSkills" },
{ "data": "feedbackType" },
{ "data": "feedbackId" },
{ "data": "isNew" },
]
});
Which is creating my datatable layout and I am calling below function on dropdown change event is :
function loadFeedback(){
viewdatatabJS = $('#dataTablesFeedback').dataTable({
"processing" : true,
"retrieve" : true,
"ajax" : "/nhp/rest/feedback/viewFeedback",
"fnServerParams": function ( aoData ) {
aoData.push( { "name": "userName", "value":employeeId } ,
{ "name": "resourceId", "value":mentorDataJson[$('#dropDownId').val()].resourceId });
},
});
}
Where I am passing some parameter in aoData.push but my URL is not getting called.
I Solved the issue by simply implementing datatable properties. i wrote my code of datatable
var viewdatatab = $('#dataTablesFeedback').dataTable({
"columns": [
{ "data": "resourceId" },
{ "data": "feedbackRecommendation" },
{ "data": "technicalSkillGaps" },
{ "data": "technicalAvgSkills" },
{ "data": "feedbackType" },
{ "data": "feedbackId" },
{ "data": "isNew" },
]
});
in jsp document.ready(function()) and then on my request call of drop down change event i wrote below code on my javascript function.
$.ajax({
url : "",
type: 'GET',
contentType: "application/json",
data: {
'userName': value,
'resourceId' : value,
},
success: function(data) {
var table = $('#dataTablesFeedback').DataTable();
table.clear();
table.rows.add(data.data);
table.draw();
});
this way i first clear my datatable and then redraw it using my json which i got from my ajax call.
Thanks

Categories

Resources