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);
}
Related
I have to write map function based on response data but response data is not an array
example response
{
"data": {
"e680c823-895b-46f0-b0a0-5f8c7ce57cb2": {
"id": 98218,
......
},
"e24ed385-0b86-422e-a4cc-69064846e13b": {
"id": 98217,
.......
},
"c1cc583b-a2be-412b-a286-436563984685": {
"id": 118868,
.....
},
}
var posts = data.map(function (item) {
return item.id;
});
I have to achieve like this console
console.log( posts[1].id ) //98217
map over the Object.values and return the nested object.
const data = {
data: {
"e680c823-895b-46f0-b0a0-5f8c7ce57cb2": {
"id": 98218
},
"e24ed385-0b86-422e-a4cc-69064846e13b": {
"id": 98217
},
"c1cc583b-a2be-412b-a286-436563984685": {
"id": 118868
}
}
};
const posts = Object.values(data.data).map(obj => obj);
console.log(posts[1].id);
Object.values() returns an array of Object values. You can loop over them to get your desired result.
let respData = {
"data": {
"e680c823-895b-46f0-b0a0-5f8c7ce57cb2": {
"id": 98218
},
"e24ed385-0b86-422e-a4cc-69064846e13b": {
"id": 98217
},
"c1cc583b-a2be-412b-a286-436563984685": {
"id": 118868
}
}
}
var posts = Object.values(respData['data']).map(function (item) {
return item;
});
console.log(posts[0].id) //98218
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.
} );
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.
})
})
}
I have an API End point that i am trying to assign variables to, now the one JSON data is an array and I Loop over it to get the data out in my console log, the difficulty i am having is that i want to assign variables to them.
Here is my code:
const request = require('request');
request('https://fantasy.premierleague.com/api/leagues-classic/1114549/standings/?page_new_entries=1&page_standings=1&phase=1', { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
var data = body.standings.results;
data.forEach(obj => {
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`);
});
console.log('-------------------');
});
});
and here is my JSON data:
{
"league": {
"id": 1114549,
"name": "The crew",
"created": "2020-09-11T17:36:20.083556Z",
"closed": false,
"max_entries": null,
"league_type": "x",
"scoring": "c",
"admin_entry": 3523866,
"start_event": 1,
"code_privacy": "p",
"rank": null
},
"new_entries": {
"has_next": false,
"page": 1,
"results": []
},
"standings": {
"has_next": false,
"page": 1,
"results": [
{
"id": 30771462,
"event_total": 8,
"player_name": "Mohammed Ismail",
"rank": 1,
"last_rank": 0,
"rank_sort": 1,
"total": 8,
"entry": 3808290,
"entry_name": "Moe"
}
Now I am trying to console log only the standings.result.player_name in my console log so i can use it else where, how do i do that
So my output in the console should only be "player_name": "Mohammed Ismail",
I'm not sure that i get the question, but in case if you want to get all player_name and collect it in array as example, You can do it next:
const request = require('request');
const url = 'https://fantasy.premierleague.com/api/leagues-classic/1114549/standings/?page_new_entries=1&page_standings=1&phase=1';
async function getStandings(url) {
return new Promise((resolve, reject) => {
request(
url,
{ json: true },
(err, res, body) => {
if (err) {
reject(err);
return;
}
resolve(body.standings.results);
}
);
});
}
(async () => {
const data = await getStandings(url);
// here you will receive array of stadings
console.log('data : ', data);
})();
I have this code to fill an array with info about all books a person owns:
async getAllInfo(person_id)
{
let book_list = await this.getBooks(person_id);
for(let book in book_list)
{
book_list[book]["book_info"] = await this.getBookInfo(book_list[book]["book_id"])
}
return book_list;
}
When I run the getBooks I get:
[
{
"name": "BookA",
"book_id" : "123"
},
{
"Name": "BookB",
"book_id" : "456"
}
]
And then I complete the info about the book in the for loop:
[
{
"name": "BookA",
"book_id" : "123",
"book_info": {
"author": "authorA",
"publish_year": 1900
}
},
{
"name": "BookB",
"book_id" : "456",
"book_info": {
"author": "authorB",
"publish_year": 1900
}
}
]
The getBooks and getBookInfo are http calls and when a person have lots of books it may take some time to get all the information. Is is possible to get it simultaneously for all books in the array? I tried to remove the await and use the Promise.all(), but I always get:
[
{
"name": "BookA",
"book_id" : "123",
"book_info": {
"domain": {
"domain": null,
"_events": {},
"_eventsCount": 1,
"members": []
}
}
},
{
"name": "BookB",
"book_id" : "456",
"book_info": {
"domain": {
"domain": null,
"_events": {},
"_eventsCount": 1,
"members": []
}
}
}
]
What you're doing in this piece of code can be optimised:
for(let book in book_list) {
book_list[book]["book_info"] = await this.getBookInfo(book_list[book]["book_id"])
}
Because for each book your making a request and waiting for this request to complete before fetching the details of the next book. Here is how you can perform all the requests at once and wait for all the details of the books to be retrieved:
async getAllInfo(person_id) {
let book_list = await this.getBooks(person_id);
// Creating an array of Promises to fetch the details of all the books simultaneously
const fetchingBooksDetails = book_list.map(book => this.getBookInfo(book.book_id));
// Wainting for the requests to complete
const booksDetails = await Promise.all(fetchingBooksDetails);
// Saving the details of the book in the book_list variable
booksDetails.forEach((bookDetails, index) => {
book_list[index].book_info = bookDetails;
});
return book_list;
}
Try something like this (not tested)
async getAllInfo(person_id)
{
let book_list = await this.getBooks(person_id);
for(let book in book_list)
{
book_list[book]["book_info"] = this.getBookInfo(book_list[book]["book_id"])
}
await Promise
.all(book_list[book].map(b => b.book_info))
.then(infos => {
for (book in infos)
book_list[book]["book_info"] = infos[book];
});
return book_list;
}
function getAllInfo(person_id){
return getBooks(person_id).then(function(book_list){
var promiseArray = []
book_list.forEach(function(book,index){
promiseArray.push(getBookInfo(book_list[index]["book_id"]))
})
return Promise.all(promiseArray).then(function(values){
book_list.forEach(function(book,index){
book_list[index]["book_info"] = values[index]
})
return book_list
})
})
}
function getBooks(person_id){
return new Promise(function(resolve,reject){
resolve(
[
{
"name": "BookA",
"book_id" : "123"
},
{
"Name": "BookB",
"book_id" : "456"
}
]
)
})
}
function getBookInfo(book_id){
return new Promise(function(resolve,reject){
if(book_id=="123"){
resolve({
"author": "authorA",
"publish_year": 1900
})
}
else if(book_id=="456"){
resolve({
"author": "authorB",
"publish_year": 1900
})
}
else{
reject()
}
})
}
getAllInfo("rap_mosnter").then(function(data){
console.log(data)
})