I am trying to do data visualization using javascript to construct bar graphs. I am encountering problems when trying to render the graphs. I keep getting the message 'TypeError: rates is not iterable' in the console which refers to the for loop in the render function. Any idea how to fix this problem?
Thanks!
Here's my code:
console.log('running js for currency rates');
doFetch()
function doFetch(){
fetch('https://api.exchangeratesapi.io/latest')
.then(response => response.json())
.then(data => {
console.log('Got the data!');
console.log(data);
let rates = data.rates;
let base = data.base;
let date = data.date;
console.log(rates);
render(rates)
});
}
function render(rates){
const currencyName = Object.keys(rates);
const values = Object.values(rates);
let chart = document.querySelector('.BarContainer');
chart.innerHTML = "";
for(let rate of rates){
let bar = document.createElement('div');
let baseHeight = 100;
bar.classList.add('Bar');
bar.style.height = baseHeight + 'px';
bar.textContent = currencyName;
chart.appendChild(bar);
}
}
for/of works on iterables, and an Object isn't iterable. You might be looking for for/in.
for/of
for/in
Because rates are literal object. And for of works for Javascript Array. I think you need to use for in instead.
Here is what should be
<!doctype html>
<html>
<body>
<div class="BarContainer"></div>
</body>
<script type='text/javascript'>
console.log('running js for currency rates');
doFetch()
function doFetch(){
fetch('https://api.exchangeratesapi.io/latest')
.then(response => response.json())
.then(data => {
console.log('Got the data!');
console.log(data);
let rates = data.rates;
let base = data.base;
let date = data.date;
console.log(rates);
render(rates)
});
}
function render(rates){
const currencyName = Object.keys(rates);
const values = Object.values(rates);
let chart = document.querySelector('.BarContainer');
chart.innerHTML = "";
for(let rate in rates){
let bar = document.createElement('div');
let baseHeight = 100;
bar.classList.add('Bar');
bar.style.height = baseHeight + 'px';
bar.textContent = currencyName;
chart.appendChild(bar);
}
}
</script>
<html>
Related
So I'm coding this
https://vercel.com/eduardodevolmedo/jsd-aily
function executeWeekly() {
fetch("/data.json")
.then(function (resp) {
return resp.json();
})
.then(function (data) {
currentW = data[0].timeframes.weekly.current
previousW = data[0].timeframes.weekly.previous
currentWork.innerHTML = `${currentW}${hrs}`
previousWork.innerHTML = `${lastWeek} - ${previousW}${hrs}`
//DATA FROM PLAY
currentP = data[1].timeframes.weekly.current
previousP = data[1].timeframes.weekly.previous
currentPlay.innerHTML = `${currentP}${hrs}`
previousPlay.innerHTML = `${lastWeek} - ${previousP}${hrs}`
//DATA FROM STUDY
currentS = data[2].timeframes.weekly.current
previousS = data[2].timeframes.weekly.previous
currentStudy.innerHTML = `${currentS}${hrs}`
previousStudy.innerHTML = `${lastWeek} - ${previousS}${hrs}`
//DATA FROM EXERCISE
currentE = data[3].timeframes.weekly.current
previousE = data[3].timeframes.weekly.previous
console.log(currentE, previousE)
currentExercise.innerHTML = `${currentE}${hrs}`
previousExercise.innerHTML = `${lastWeek} - ${previousE}${hrs}`
//DATA FROM SOCIAL
currentSO = data[4].timeframes.weekly.current;
previousSO = data[4].timeframes.weekly.previous;
currentSocial.innerHTML = `${currentSO}${hrs}`
previousSocial.innerHTML = `${lastWeek} - ${previousSO}${hrs}`
//DATA FROM SELFCARE
currentSE = data[5].timeframes.weekly.current;
previousSE = data[5].timeframes.weekly.previous;
currentSelfcare.innerHTML = `${currentSE}${hrs}`
previousSelfcare.innerHTML = `${lastWeek} - ${previousSE}${hrs}`
})
eachBlock.forEach(el => el.classList.toggle('animatedBox'))
}
function executeDaily() {
fetch("/data.json")
.then(function (resp) {
return resp.json();
})
.then(function (data) {
//DATA FROM WORK
currentW = data[0].timeframes.daily.current
previousW = data[0].timeframes.daily.previous
currentWork.innerHTML = `${currentW}${hrs}`
previousWork.innerHTML = `${lastDay} - ${previousW}${hrs}`
//DATA FROM PLAY
currentP = data[1].timeframes.daily.current
previousP = data[1].timeframes.daily.previous
currentPlay.innerHTML = `${currentP}${hrs}`
previousPlay.innerHTML = `${lastDay} - ${previousP}${hrs}`
//DATA FROM STUDY
currentS = data[2].timeframes.daily.current
previousS = data[2].timeframes.daily.previous
currentStudy.innerHTML = `${currentS}${hrs}`
previousStudy.innerHTML = `${lastDay} - ${previousS}${hrs}`
//DATA FROM EXERCISE
currentE = data[3].timeframes.daily.current
previousE = data[3].timeframes.daily.previous
currentExercise.innerHTML = `${currentE}${hrs}`
previousExercise.innerHTML = `${lastDay} - ${previousE}${hrs}`
//DATA FROM SOCIAL
currentSO = data[4].timeframes.daily.current;
previousSO = data[4].timeframes.daily.previous;
currentSocial.innerHTML = `${currentSO}${hrs}`
previousSocial.innerHTML = `${lastDay} - ${previousSO}${hrs}`
//DATA FROM SELFCARE
currentSE = data[5].timeframes.daily.current;
previousSE = data[5].timeframes.daily.previous;
currentSelfcare.innerHTML = `${currentSE}${hrs}`
previousSelfcare.innerHTML = `${lastDay} - ${previousSE}${hrs}`
})
eachBlock.forEach(el => el.classList.toggle('animatedBox'))
}
So, im running the exact same function at monthly, and daily.
Which is now working, but i had to copy the same code for the same functions, so I was thinking in a way I could do it in less lines of code. So, my idea was to assign a string to a variable, and then making it a string, using JSON.stringify()
let z = weekly
let x = JSON.stringify(z)
Then i thought, that I would assing a variable to each button on html, using an if statement, for example if the button value was "daily" then z would be daily, and the function would run using daily as that argument.
And then, I just add it like a variable, depending on what i want:
currentW = data[0].timeframes.z.current
instead of:
currentW = data[0].timeframes.daily.current
In that way, I would only need to use one function.
But that doesn't seems to work.
How can I do this? Is there any way?
If you want to check the code further:
https://github.com/EduardoDevOlmedo/JSDaily
You have to access the object member as if the object was an associative array:
currentW = data[0].timeframes[z].current
Solved:
Accessing to
this.innerHTML
and assigning the value to z.
function check(){
value = this.innerHTML
execute(value)
}
I'm trying to build a simple MagicMirror in-browser weather app based on openweathermap API.
Knowing very little about coding, it proves a bit hard. I have this code, that someone corrected for me, but it won't run on older devices. Here's the sandbox link.
class Weather {
constructor(data) {
this.data = data;
this.temp = Math.round(data.main.temp);
this.feels_like = Math.round(data.main.feels_like);
this.description = data.weather[0].description;
this.city = data.name;
this.country = data.sys.country;
this.wind = Math.round(data.wind.speed);
this.humidity = data.main.humidity;
}
geticonClass() {
let prefix = this.data.weather[0].icon.endsWith("d")
? 'wi-owm-day-'
: 'wi-owm-night-';
return `${prefix}${this.data.weather[0].id}`;
}
}
function fetchWeather() {
navigator.geolocation.getCurrentPosition(
async (position) => {
let url = new URL("https://api.openweathermap.org/data/2.5/weather");
url.searchParams.set("lat", position.coords.latitude);
url.searchParams.set("lon", position.coords.longitude);
url.searchParams.set("lang", "pl");
url.searchParams.set("appid", "fb7164d50e0faf1f058561b7903f03b9");
url.searchParams.set("units", "metric");
const response = await fetch(url);
const weatherJSON = await response.json();
updateDOM(new Weather(weatherJSON));
},
(error) => {
console.error("Unable to get geolocation, Unsuported maybe?");
}
);
}
function updateDOM(weather) {
const iconElement = document.querySelector(".today-weather-icon i");
const tempElement = document.querySelector(".temperature-value p");
const tempFeelElement = document.querySelector(".temperature-feel p");
const descElement = document.querySelector(".temperature-description p");
const locationElement = document.querySelector(".location p");
const windElement = document.querySelector(".wind p");
const humidElement = document.querySelector(".humid p");
iconElement.classList.add(weather.geticonClass());
tempElement.innerHTML = `${weather.temp}°<span>C</span>`;
tempFeelElement.innerHTML = `Odczuwalna: ${weather.feels_like}°<span>C</span>`;
descElement.innerHTML = weather.description;
locationElement.innerHTML = `${weather.city}, ${weather.country}`;
windElement.innerHTML = ` ${weather.wind} km/h`;
humidElement.innerHTML = ` ${weather.humidity}`;
}
fetchWeather();
setInterval(fetchWeather, 1800000);
updateDOM();
setInterval(updateDOM, 1820000);
Here's a previous version of the code with navigator.geolocation that worked.
You are using modern ES6 syntax for example class Weather that's why. If you want to learn more about how you can convert it to pre ES6 syntax so it works on older devices too check this article ES6 Class vs Object.prototyoe
`
I am trying to get API data so i can render it on Webpage using ReactJS. I tried many different ways to fetch api where its stored in array. But i am unable to do it.
Note:https://www.hatchways.io/api/assessment/workers/<worker_id> Here i'm looping so the
worker id gets add the of url.
const fetchAPI = e => {
let array = [];
const api2 = `https://www.hatchways.io/api/assessment/workers/`;
for (var i = 0; i <= 4; i++) {
array.push(api2 + i);
}
return array;
};
console.log(fetchAPI());
Thanks in advance.
You need to hit the url first. Async/Await would be a good choice.
<script>
async function check()
{
var arrayData =[];
var url = "https://www.hatchways.io/api/assessment/workers/";
for(var i=1;i<=4;i++)
{
const response = await fetch(url+""+i);
const myJson = await response.json();
arrayData.push(myJson);
}
console.log(arrayData)
}
check();
</script>
Use Promise.all for example:
const baseURL = 'https://jsonplaceholder.typicode.com';
const fetchUsers = fetch(`${baseURL}/users`);
const fetchPosts = fetch(`${baseURL}/posts`);
Promise.all([fetchUsers, fetchPosts]).then((responses) => {
const responsesToJson = responses.map(response => response.json());
return Promise.all(responsesToJson);
}).then((jsonResponse) => {
const [userResponse, postResponse] = jsonResponse;
console.log(userResponse);
console.log(postResponse);
});
Mediocre javascript developer here and need some help..
I want to make a GET call to a url several times in a for loop.
I am trying to use fetch, but because of the promise/timing situation, I can't figure out how to make it work.
Below is the code and it's entirely possible that the fetch approach doesn't make sense for what I am trying to do. Would appreciate your help either helping me with code or telling me I am idiot and advising an alternative :)
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD","EUR","INR","GBP","SGD"];
var adjAmt = [];
async function getConversionAsync(fcur,tcur,amt)
{
let response = await fetch('https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=' + fcur + '&to=' + tcur + '&amount=' + amt);
let data = await response.json()
return data;
}
for (i = 0; i < toCur.length; i++) {
getConversionAsync(fromCur,toCur[0].toString(),fromAmt)
.then(data => display(data));
}
function display(thing){
adjAmt.push(thing.result);
}
document.getElementById("something").innerHTML = adjAmt[0].toString();
In your example, document.getElementById("something").innerHTML = adjAmt[0].toString(); is executed before anything is pushed to adjAmt. You need to wait for the loop calls to finish before displaying a result, and for this you could wrap everything inside an async function.
const fromAmt = 100;
const fromOOP = 50;
const fromGM = 50;
const fromCur = 'USD';
const toCur = ['USD', 'EUR', 'INR', 'GBP', 'SGD'];
const adjAmt = [];
const getConversionAsync = async (fcur, tcur, amt) => {
const response = await fetch(`https://data.fixer.io/api/convert?access_key=xyxyxyxyxyxyxy&from=${fcur}&to=${tcur}&amount=${amt}`);
return response.json();
}
function display(thing) {
adjAmt.push(thing.result);
}
(async () => {
for (i = 0; i < toCur.length; i += 1) {
const data = await getConversionAsync(fromCur, toCur[0], fromAmt);
display(data);
}
document.getElementById('something').innerHTML = adjAmt[0].toString();
})();
Some small changes to make it work without the API call, but you'll want to access the correct index in your loop. I don't know what the exact output you're wanting here but in this case I just joined all the values in the array.
Additionally, the setting of innerHTML needs to be done once all the values are retrieved from the API, so I would even suggest doing that when the loop terminates, or some other "done" type event.
Additionally, you can use Promise.all instead of a loop, which is what I would go with personally.
var fromAmt = 100;
var fromOOP = 50;
var fromGM = 50;
var fromCur = "USD"
var toCur = ["USD", "EUR", "INR", "GBP", "SGD"];
var adjAmt = [];
async function getConversionAsync(fcur, tcur, amt) {
let response = await sampleRequest()
/* let data = await response.json() */
return response;
}
for (i = 0; i < toCur.length; i++) {
const data = getConversionAsync(fromCur, toCur[i].toString(), fromAmt).then(data => {
display(data)
})
}
function display(thing) {
adjAmt.push(thing);
document.getElementById("something").innerHTML = adjAmt.join(', ')
}
function sampleRequest() {
return new Promise((resolve, reject) => {
resolve(Math.round(Math.random() * 1000))
})
}
<div id="something"></div>
I'm practicing my javascript skills doing a joke machine that collects jokes from an API and displays them, my problem is that currently, I'm using .then() which is fine, but I want to know how do I get my results in an array that doesn't have to wait for .then() like in fetchJokes().then(jokes => console.log(jokes));
I used to have a function that navigated the array like this:
let counter = 0;
let jokes = [joke1, joke2, joke3, joke4];
function nextJoke(jokes) {
counter++;
counter %= jokes.length; // start from 0 if we get to the end of the array
return jokes[counter]; // give us back the actual item
}
That's how it was used before fetching from the API.
And this is my current code:
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
async function fetchJokes() {
let jokes = [];
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
jokes[i] = data.value[i].joke;
}
return jokes;
}
fetchButton.addEventListener("click", () => {
fetchJokes().then(jokes => console.log(jokes));
});
<button type="button" value="Fetch">Fetch Jokes</button>
So I have figured out how to use buttons and all that jazz, just asking for help on how to pass the object that im receiving from fetchJokes() and store it in an Array.
You can use fetch() to retrieve the url's data. Then you have to transform it to json with .then((resp) => resp.json()).
After that, you can store the data inside your jokes array.
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
const nextButton = document.querySelector("button[type=button][value=Next]");
const display = document.getElementById("display");
let counter = 0;
let jokes = [];
nextButton.style.visibility = 'hidden'; //Hide next button
fetchButton.addEventListener("click", () => {
fetch(url).then((resp) => resp.json()) // Transform the data into json
.then(data => (jokes = data.value, //Set the array of jokes
fetchButton.style.visibility = 'hidden', //Hide fetch button
nextButton.style.visibility = 'visible')); //Show next button
});
nextButton.addEventListener("click", () => {
//Show next joke
display.innerHTML = jokes[counter++ % jokes.length].id + " : " + jokes[counter++ % jokes.length].joke;
});
<button type="button" value="Fetch">Fetch Jokes</button>
<button type="button" value="Next">Next joke</button>
<p id="display"></p>
This is what worked for me without modifying the main purpose of the snippet in the question.
let counter = 0;
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button][value=Fetch]");
async function fetchJokes() {
let jokes = [];
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
jokes[i] = data.value[i].joke;
}
return jokes;
}
fetchButton.addEventListener("click", () => {
fetchJokes().then(data => (jokes = data)).then(data => console.log(data));
});
<button type="button" value="Fetch">Fetch Jokes</button>
I'm not sure I understand the question...If you want to fetch from an async api and not have to use .then or have to wait, then that is not possible.
From what I understand of the question, if you do not want to use .then every time, only fetch when the array is empty (only fetch once):
const url = "https://api.icndb.com/jokes/random/5";
const fetchButton = document.querySelector("button[type=button]
[value=Fetch]");
let arr = []
let counter = 0;
async function fetchJokes() {
let data = await (await fetch(url)).json();
for (let i = 0; i < 5; i++) {
arr[i] = data.value[i].joke;
}
}
function nextJoke(jokes) {
const item = jokes[counter]
counter++;
if (counter >=jokes.length){
counter %= jokes.length;
}// start from 0 if we get to the end of the array
return item; // give us back the actual item
}
fetchButton.addEventListener("click", () => {
if (arr.length == 0){
fetchJokes()
.then(()=>console.log(nextJoke(arr)))
}else{
console.log(nextJoke(arr))
}
});
DEMO codepen
There are many ways to do this. I would suggest looking more into Javascript Promises.