Javascript Fetch API not showing results on multiple cards - javascript

I have a script that fetches the data from a link and then inserts that in tables and shows it to the user. the script worked fine. but then I decided to make the function more dynamic so I added 2 arguments name and Html div name where the data will display. it worked fine when I called the function the first time but it doesn't show any data on the second call. I'm using PHP for my projects and new to javascript. but I made a similar function in PHP but that works. any suggestions?
function getInfoByCountry(name, displayName) {
fetch("https://something.com/countries/" + name)
.then(res => res.json())
.then(data => {
output = `
<tr>
<td>Total Cases</td>
<td>${data.cases}</td>
</tr>
<tr>
<td>Today Cases</td>
<td>${data.todayCases}</td>
</tr>
<tr>
<td>Deaths</td>
<td>${data.deaths}</td>
</tr>
<tr>
<td>Recovered</td>
<td>${data.recovered}</td>
</tr>
<tr>
<td>Critical</td>
<td>${data.critical}</td>
</tr>
<tr>
<td>Active Cases</td>
<td>${data.active}</td>
</tr>
`;
document.getElementById(displayName).innerHTML = output;
});
}
getInfoByCountry("USA", "USAInfo");
getInfoByCountry("China", "ChinaInfo");
<div class="table-responsive">
<table class="table table-striped mb-0">
<tbody id="ChinaInfo">
</tbody>
</table>
</div>

Add return like this
function getInfoByCountry(name, displayName) {
var display = document.getElementById(displayName);
return fetch("https://smthing.com/countries/"+name)
.then(res => res.json())
.then(data => {
output = `
<tr>
<td>Total Cases</td>
<td>${data.cases}</td>
</tr>
<tr>
<td>Today Cases</td>
<td>${data.todayCases}</td>
</tr>
<tr>
<td>Deaths</td>
<td>${data.deaths}</td>
</tr>
<tr>
<td>Recovered</td>
<td>${data.recovered}</td>
</tr>
<tr>
<td>Critical</td>
<td>${data.critical}</td>
</tr>
<tr>
<td>Active Cases</td>
<td>${data.active}</td>
</tr>
`;
document.getElementById(displayName).innerHTML += output;
}
);
}
getInfoByCountry("USA", "display");
getInfoByCountry("China", "display");

you just need to add return statement for returning output.
here is fiddle you can check function.
function getInfoByCountry(name,displayName) {
debugger;
return fetch("https://jsonplaceholder.typicode.com/posts/"+name)
.then(res => res.json())
.then(data => {
output = `
<tr>
<td>Total Cases</td>
<td>${data.title}</td>
</tr>
<tr>
<td>Today Cases</td>
<td>${data.body}</td>
</tr>
`;
document.getElementById(displayName).innerHTML = output;
});
}

You are adding the result using displayName as id attribute, you should add another one for USAinfo as mentioned in the comments, or a better implementation would be to create the table dynamically and append the result to the container.
function getInfoByCountry(name, displayName) {
fetch("API_URL" + name)
.then(res => res.json())
.then(data => {
output = `
<table class="table table-striped mb-0">
<tbody>
<tr>
<td>Total Cases</td>
<td>${data.cases}</td>
</tr>
<tr>
<td>Today Cases</td>
<td>${data.todayCases}</td>
</tr>
<tr>
<td>Deaths</td>
<td>${data.deaths}</td>
</tr>
<tr>
<td>Recovered</td>
<td>${data.recovered}</td>
</tr>
<tr>
<td>Critical</td>
<td>${data.critical}</td>
</tr>
<tr>
<td>Active Cases</td>
<td>${data.active}</td>
</tr>
</tbody>
</table><hr />
`;
document.getElementById("container").innerHTML += output;
});
}
getInfoByCountry("USA", "USAInfo");
getInfoByCountry("China", "ChinaInfo");
table, td {
border: 1px solid;
}
<div id="container" class="table-responsive">
</div>

Related

How can I get specific data from html in React Native?

const fetchData = async () => {
fetch('https://*******/*****search-value={value}')
.then(response => response.text())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
}
I succesfully got the data from the api (it returns full html), and I got this response:
<div class="col-md-12">
<p class="text-info strong">
Succes.
</p>
<table class="table">
<tr>
<td width="40%">Number:</td>
<td>900000000053901</td>
</tr>
<tr>
<td>ID</td>
<td></td>
</tr>
<tr>
<td>Name:</td>
<td>Grafit</td>
</tr>
<tr>
<td>Spiece:</td>
<td>kutya</td>
</tr>
<tr>
<td>Type:</td>
<td>Border collie</td>
</tr>
<tr>
<td>Gender:</td>
<td>hím</td>
</tr>
<tr>
<td>Last modified:</td>
<td></td>
</tr>
<tr>
<td>Dangerous:</td>
<td>
Nem
</td>
</tr>
<tr>
<td>Last vaccine:</td>
<td>Intervet Nobivac DHPPi+RL, 2018.02.17</td>
</tr>
<tr>
<td>isDead:</td>
<td>
Nem
</td>
</tr>
</table>
Now I want to see the data from class="text-info strong" if Succes or not.
If Succes I want to read the html table () and parse it to a json.
{
Number: 900000000053901,
ID:"",
Name:"Grafit",
...
}
How can I achieve this?
You could do it with something like this:
function tableToJson(table) {
const data = [];
for (let i = 1; i < table.rows.length; i++) {
const tableRow = table.rows[i];
const rowData = {};
rowData[tableRow.cells[0].textContent] =
tableRow.cells[1].textContent.replace(/\n\ +/gm, '');
data.push(rowData);
}
return data;
}
const dataHTML = document.createElement('div');
dataHTML.innerHTML = data;
const isSuccess =
dataHTML.querySelector('.text-info.strong').textContent === 'success';
if(!isSuccess) return false;
const table = dataHTML.getElementsByTagName('table')[0];
return tableToJson(table);

Javascript grab the data from the table in the HTML and build an array of objects that contains the table data

I have an HTML table and I need to define a function that should grab the data from the table and build an array of objects that contains table data. Outside the function I have to declare a variable and assign the returned value from the function.
Thanks in advance.
HTML
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
JS
function buildTableData() {
let tbody = document.getElementsByTagName("tbody")[0];
let rows = tbody.children;
let people = [];
for (let row of rows) {
let person = {};
let cells = row.children;
person.rating = cells[0].textContent;
person.review = cells[1].textContent;
person.favoriteFood = cells[2].textContent;
people.push(person);
return people;
}
let data = people;
console.log(data);
}
You can get all the elements by using querySelectorAll('td'). Then use map to to get only the text of it and return this.
function buildTableData() {
const elements = [...document.querySelectorAll('td')];
return elements.map(x => {
return {content : x.innerHTML}
});
}
console.log(buildTableData());
<body>
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/7.3.1/acorn.js" integrity="sha512-4GRq4mhgV43mQBgKMBRG9GbneAGisNSqz6DSgiBYsYRTjq2ggGt29Dk5thHHJu38Er7wByX/EZoG+0OcxI5upg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn-walk/7.2.0/walk.js" integrity="sha512-j5XDYQOKluxz1i4c7YMMXvjLLw38YFu12kKGYlr2+w/XZLV5Vg2R/VUbhN//K/V6LPKuoOA4pfcPXB5NgV7Gwg==" crossorigin="anonymous"></script>
<script src="index.js"></script>
</body>
You can try using querySelectorAll() and map() like the following way:
function buildTableData() {
let rows = document.querySelectorAll('tbody tr');
let data = Array.from(rows).map(function(tr){
return {
rating: tr.querySelectorAll('td:nth-child(1)')[0].textContent,
review: tr.querySelectorAll('td:nth-child(2)')[0].textContent,
favoriteFood: tr.querySelectorAll('td:nth-child(3)')[0].textContent
};
});
console.log(data);
}
buildTableData();
<h2>Product reviews</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Rating</th>
<th>Review</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>5/5</td>
<td>This product is so good, I bought 5 more!</td>
</tr>
<tr>
<td>Jane</td>
<td>4/5</td>
<td>Good value for the price.</td>
</tr>
<tr>
<td>David</td>
<td>1/5</td>
<td>Arrived broken :(</td>
</tr>
<tr>
<td>Fiona</td>
<td>5/5</td>
<td>I love it!</td>
</tr>
<tr>
<td>Michael</td>
<td>3/5</td>
<td>Doesn't live up to expectations.</td>
</tr>
</tbody>
</table>
You want a loop, and each review to be an object that is appended to an array of reviews is what I'm assuming
var reviews = [];
var tbody = document.querySelectorAll("tbody")[0];
var TRs = tbody.querySelectorAll("tr");
for (var a = 0; a < TRs.length; a++) {
var TDs = TRs[a].querySelectorAll("td");
var review = {
name: "",
rating: "",
review: ""
};
//These assume the order of your table columns don't change
review.name = TDs[0].innerHTML;
review.rating = TDs[1].innerHTML;
review.review = TDs[2].innerHTML;
reviews.push(review);
}
Your reviews array should have everything in there just as you wanted. I assumed the third column was "review" instead of "favorite food"

cant generate the PDF(with all details) using html-pdf in NodeJS

I am a bit stuck when creating the PDF from the html template. if i use the static entries it does work but i am looking to generate the pdf for multiple items and the items can vary each time, i am not sure if i am missing something any suggestion guys please.
after i added .map function in the pdfTemplate.js file as below to go through each object and get the price, it seems to not working properly
snippet from the main ./documents/pdfTemplate Js file
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return ` <tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})`
Note: PDF is still generating but missing every bit that i included after the .map function
main ./documents/pdfTemplateJs file
module.exports = (customer_dataand_Itemsbought) => {
//customer_dataand_Itemsbought is array of objects[obj[0],obj[1],...] and at obj[0] is the customer details object and after that each index object contains the customer bought items detail/price
let customer_Name = customer_dataand_Itemsbought[0].user_FirstName;
let receipt_Id = customer_dataand_Itemsbought[0].Invoice_No_latest;
let total_Price;
customer_dataand_Itemsbought.shift();
const today = new Date();
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>PDF Result Template</title>
<style>
........
</style>
</head>
<body>
<div class="invoice-box">
<table cellpadding="0" cellspacing="0">
<tr class="top">
<td colspan="2">
<table>
<tr>
<td class="title"><img src="https://i2.wp.com/cleverlogos.co/wp-content/uploads/2018/05/reciepthound_1.jpg?fit=800%2C600&ssl=1"
style="width:100%; max-width:156px;"></td>
<td>
Datum: ${`${today.getDate()}. ${today.getMonth() + 1}. ${today.getFullYear()}.`}
</td>
</tr>
</table>
</td>
</tr>
<tr class="information">
<td colspan="2">
<table>
<tr>
<td>
Customer name: ${customer_Name} // this still appears on the PDF
</td>
<td>
Receipt number: ${receipt_Id} // this still appears on the PDF
</td>
</tr>
</table>
</td>
</tr>`;
// here i used the .map function to go through each array object. nothing appears from here onwards on the pdf
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return ` <tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})`
</table>
<br />
<h1 class="justify-center">Total price: ${total_Price}£</h1>
</div>
</body>
</html>
`;
};
at server.js side
const pdf = require("html-pdf");
const pdfTemplate = require("./documents/pdfTemplate");
pdf.create(pdfTemplate(customer_dataand_Itemsbought), {}).toFile("./filename.pdf", function (err, res) {
if (err) {
console.log(err);
}
});
When you have enclosed map function's body in curly braces - meaning you have multiple statements for processing data, you are expected to return from it explicitly:
customer_dataand_Itemsbought.map((eachitem) => {
total = total + eachitem.price;
return `<tr class="heading">
<td>Bought items:</td>
<td>Price</td>
</tr>
<tr class="item">
<td>First item:</td>
<td>${eachitem.price}$</td>
</tr>
<tr class="item">
<td>Second item:</td>
<td>${eachitem.price}$</td>
</tr>`;
})

How to make a number with commas as thousands separator in table, javascript

I want to print an integer with a comma as a separator, for example 10,234,234
in the price column.
I have made the function, but I am a little confused how to make it in the table. Below is my code
<table class="table">
<tr>
<th> Item</th>
<th>Price</th>
</tr>
#foreach (SHOP.ViewModels.ItemViewModel item in Model.Itemss)
{
<tr>
<td> #Html.DisplayFor(modelitem => item.itemName) </td>
<td> #Html.DisplayFor(modelitem => item.itemPrice)</td>
</tr>
}
<tr>
<td></td>
<td> #Html.EditorFor(model => model.TotalPrice, new { htmlAttributes = new { #class = "form-control", #onchange = "validationCheck();", #readonly = "readonly" } }) </td>
</tr>
</table>
<script>
function numberSeparator(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
</script>
You need to get all second td elements in the table and then update the content using the code.
<table class="table" id="table">
<!-- set id to get table^^^^^^^^---->
<tr>
<th> Item</th>
<th>Price</th>
</tr>
#foreach (SHOP.ViewModels.ItemViewModel item in Model.Itemss)
{
<tr>
<td> #Html.DisplayFor(modelitem => item.itemName) </td>
<td> #Html.DisplayFor(modelitem => item.itemPrice)</td>
</tr>
}
</table>
<script>
function numberSeparator(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// get all second tds
[...document.querySelectorAll('#table tr > td:nth-child(2)')].forEach(e => {
// get text content and update
e.textContent = numberSeparator(e.textContent.trim());
})
</script>
FYI : It's always better to do it from the backend side if possible. There may be some functions available in your backend framework/programming language.
UPDATE : In your updated code you are creating an input so get input element update it's value.
<table class="table">
<tr>
<th> Item</th>
<th>Price</th>
</tr>
#foreach (SHOP.ViewModels.ItemViewModel item in Model.Itemss)
{
<tr>
<td> #Html.DisplayFor(modelitem => item.itemName) </td>
<td> #Html.DisplayFor(modelitem => item.itemPrice)</td>
</tr>
}
<tr>
<td></td>
<td> #Html.EditorFor(model => model.TotalPrice, new { htmlAttributes = new { #class = "form-control", #onchange = "validationCheck();", #readonly = "readonly" } }) </td>
</tr>
</table>
<script>
function numberSeparator(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// get all second tds
[...document.querySelectorAll('#table tr > td:nth-child(2) input')].forEach(e => {
// get text content and update
e.value = numberSeparator(e.value.trim());
})
</script>
You can use toLocaleString to display a number with comma separators
let num = 1000000
let display = num.toLocaleString('us-US')
console.log(display) // 1,000,000
I suspect your code could work like this
#foreach (SHOP.ViewModels.ItemViewModel item in Model.Itemss)
{
<tr>
<td> #Html.DisplayFor(modelitem => item.itemName) </td>
<td> #Html.DisplayFor(modelitem => item.itemPrice.toLocaleString('us-US'))</td>
</tr>
}

textContent and innerHTML not changing the DOM

Good morning,
I'm hoping someone can help me with this seemingly simple question. I can't figure out why my textContent or innerHTML won't update my DOM. It shows in my console that it has changed, but for some reason that escapes me I can't figure out why the DOM isn't changing. Any help is appreciated!
I have the following code
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
grabtext = 'Super User';
}
console.log(grabtext);
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
innerHTML and textContent are basically setters and getters. Getting their reference in a variable and setting value to that reference will not invoke the setter.
You need to set value directly to them
e.textContent ?
( e.textContent == 'SUPER' ? (e.textContent = 'Super User') : "" ) :
( e.innerHTML == 'SUPER' ? (e.innerHTML = 'Super User') : "" )
I have updated the snippet.
You were not assigning the Super User value to e.textContent inside if condition
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function (e) {
var grabtext = e.textContent || e.innerHTML;
if (grabtext === 'SUPER') {
e.textContent = 'Super User';
}
})
};
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Hope this will help
Nothing in your code changes what's in the DOM. Assigning to your grabText variable just changes the value of that variable, not the element you got the text from.
To do that, you'd havE to assign back to textContent/innerHTML.
Separately: innerHTML is very different from textContent. If you're going to feature-detect, use textContent and fall back to innerText, not innerHTML¹. When feature-detecting this, don't just use ||, it can pick the wrong one if the element's text is blank. Instead, look to see if the element has a property with the desired name. You only need to do it once and remember it.
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
var textPropName = "textContent" in document.createElement("div") ? "textContent" : "innerText";
function updateText() {
document.querySelectorAll('.userRoleStyle').forEach(function(e) {
if (e[textPropName] === 'SUPER') {
e[textPropName] = 'Super User';
}
})
}
table td {
padding: 10px;
}
<body>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email address</th>
<th>User Role</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>TEST1</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST2</td>
<td>test#test.com</td>
<td class="userRoleStyle">ADMINISTRATOR</td>
<td>
Test
</td>
</tr>
<tr class="userList">
<td>TEST3</td>
<td>test#test.com</td>
<td class="userRoleStyle">SUPER</td>
<td>
Test
</td>
</tr>
</tbody>
</table>
</body>
Side note: You don't put ; after function declarations. It's a statement terminator; declarations aren't statements. You'd have one after an assignment statement with a function expression, but not after a declaration. (It's harmless, though.)
¹ textContent and innerText are also different from one another, but the differences don't matter in your case.

Categories

Resources