I'm new to javascript, and I'm trying to make some changes.
I have a search code in searchBar and with it a call to an API.json to fetch the information and put it on the site. Everything works fine, however I would like to modify it to add a Promise.all and be able to call more than one API and be able to display it on the site. But all my attempts have failed.
what do i have in js:
const localList = document.getElementById('localList');
const searchBar = document.getElementById('searchBar');
let hpWords = [];
searchBar.addEventListener('keyup', (e) => {
const searchString = e.target.value.toLowerCase();
const filteredWords = hpWords.filter((words) => {
return (
words.name.toLowerCase().includes(searchString)
);
});
displayCharacters(filteredWords);
});
const loadCharacters = async () => {
try {
const res = await fetch('https://duhnunes.github.io/api/local.json');
hpWords = await res.json();
displayCharacters(hpWords);
} catch (err) {
console.error(err);
}
};
const displayCharacters = (words) => {
const htmlString = words
.map((character) => {
return `
<div class="voc-box-content">
<h5>${character.name} - ${character.trans} [${character.type} - ${character.type2}]</h5>
<p>${character.description}</p>
</div>
`;
})
.join('');
localList.innerHTML = htmlString;
};
loadCharacters();
To display on the page I am using <div id="localList"></div>. In the example I am displaying local.json and I would also like to display item.json with <div id ="itemList"></div>.
Both have the same display structure on the <div class="voc-box-content">...</div> site.
Cheers,
DuH
To fetch words from multiple apis you can use this:
const searchBar = document.getElementById('searchBar');
const lists = {
localList: {
api: 'api1'
},
miscList: {
api: 'api2'
},
...
};
Object.entries(lists).forEach(([id, list]) => {
list.element = document.getElementById(id);
});
searchBar.addEventListener('keyup', (e) => {
const searchString = e.target.value.toLowerCase();
Object.values(lists).forEach((list) => {
list.filteredWords = list.words.filter((word) => {
return word.name.toLowerCase().includes(searchString);
});
});
displayCharacters();
});
const loadCharacters = async () => {
try {
await Promise.all(
Object.values(lists).map(async (list) => {
const response = await fetch(list.api);
list.words = await response.json();
})
);
displayCharacters();
} catch (err) {
console.error(err);
}
};
const displayCharacters = () => {
Object.values(lists).forEach((list) => {
list.element.innerHTML = (list.filteredWords ?? list.words)
.map((word) => `
<div class="voc-box-content">
<h5>${word.name} - ${word.trans} [${word.type} - ${word.type2}]</h5>
<p>${word.description}</p>
</div>`)
.join('');
});
};
loadCharacters();
Related
I have an array of URLS stored within a document that i'd like to read and display as individual cards. All I'm getting is the return of the whole array, I'm not mapping it correctly and I don't know where I'm going wrong.
Currently, it's displaying "https://website1.com, https://website2.com". Where as I would like it to be individual items.
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setInternalLinks(tempData);
});
} catch (err) {
console.error(err);
};
};
useEffect(() => {
getInternalLinks()
},[])
return (
{internalLinks.map((doc, index) => {
<Card>
<p>{doc.urls.urls}</p>
</Card>
}))
);
Firebase Collection Structure
Try adding it directly to the state:
const [internalLinks, setInternalLinks] = useState([]);
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const data = doc.data();
setInternalLinks([ ...internalLinks, data ]);
});
});
} catch (err) {
console.error(err);
};
};
Here is a functon that requests images files. How to return a promise when all files are loaded?
function request() {
for (const [src, nodes] of this.icons.entries()) {
fetch(`${this.baseHref}assets/images/${src}`)
.then((res) => res.text())
.then((content: string) => {
nodes.forEach((node: LayerNode) => {
const { icon } = node;
const { color } = icon;
const replaceIcon = getReplaceIcon(this.defPointIcon, color);
const defIcon = getDefIcon(content, color);
this.cachedIcons.set(node, { defIcon, replaceIcon });
});
});
}}
I have tried this:
const promises = [];
for (const [src, nodes] of this.icons.entries()) {
promises.push(fetch(`${this.baseHref}assets/images/${src}`));
}
Then
Promise.all(promises)
.then((res) => res.text())
.then((content: string) => {
nodes.forEach((node: LayerNode) => {
const { icon } = node;
const { color } = icon;
const replaceIcon = getReplaceIcon(this.defPointIcon, color);
const defIcon = getDefIcon(content, color);
this.cachedIcons.set(node, { defIcon, replaceIcon });
});
});
Problem is I lost the nodes.
If your problem is that you lost the reference to nodes, you can make your promise to include it in the result through a closure:
const promises = [];
for (const [src, nodes] of this.icons.entries()) {
promises.push(fetch(`${this.baseHref}assets/images/${src}`).then(result => [result, nodes]));
}
Then you should change the way you read the result:
.then(([res, nodes]) => res.text().then(content => [content, nodes]))
.then(([content, nodes]: [string, any[]]) => {
I am trying to execute allCountryData and return a promise its working fine but after allCountryData is done executing I want to perform a operation on that returned data / or allCountryDataArray and store the highest values in arrayOfHighestCases
Note I can't chain the other login in allCountryData.
Please help let me know if you need any more details
export const allCountryDataArray = [];
export const arrayOfHighestCases = [];
const allCountryData = async () => {
sendHTTP()
.then((res) => {
return res.response;
})
.then((res) => {
allCountryDataArray.push(...res);
return allCountryDataArray;
});
return await allCountryDataArray;
// Highest Cases
};
The code is below is not working
const highestCasesData = async () => {
// const allCountryDataArrayy = await allCountryData();
// allCountryData()
// .then((data) => {
// console.log(arrayOfHighestCases[0]);
// })
// .then((res) => {
const np = new Promise((res, rej) => {
res(allCountryData());
});
return np.then((res) => {
console.log(res);
const arrayofHigh = allCountryDataArray.sort((a, b) => {
if (a.cases.total < b.cases.total) {
return 1;
} else if (a.cases.total > b.cases.total) {
return -1;
} else {
return 0;
}
});
console.log(arrayofHigh);
const slicedArray = arrayofHigh.slice(0, 6);
for (const eachHighCase of slicedArray) {
arrayOfHighestCases.push(eachHighCase);
}
console.log(arrayOfHighestCases);
return arrayOfHighestCases;
});
// });
};
highestCasesData();
Filling global arrays with async data is a way into timing conflicts. Bugs where the data ain't there, except when you look it is there and yet another question here on my SO about "Why can't my code access data? When I check in the console everything looks fine, but my code ain't working."
If you want to store something, store Promises of these arrays or memoize the functions.
const allCountryData = async () => {
const res = await sendHTTP();
return res.response;
};
const highestCasesData = async () => {
const allCountryDataArray = await allCountryData();
return allCountryDataArray
.slice() // make a copy, don't mutate the original array
.sort((a, b) => b.cases.total - a.cases.total) // sort it by total cases DESC
.slice(0, 6); // take the first 6 items with the highest total cases
}
This is working please let me know if I can make some more improvements
const allCountryData = async () => {
return sendHTTP()
.then((res) => {
return res.response;
})
.then((res) => {
allCountryDataArray.push(...res);
return allCountryDataArray;
});
// Highest Cases
};
const highestCasesData = async () => {
return allCountryData().then((res) => {
console.log(res);
const arrayofHigh = allCountryDataArray.sort((a, b) => {
if (a.cases.total < b.cases.total) {
return 1;
} else if (a.cases.total > b.cases.total) {
return -1;
} else {
return 0;
}
});
console.log(arrayofHigh);
const slicedArray = arrayofHigh.slice(0, 6);
for (const eachHighCase of slicedArray) {
arrayOfHighestCases.push(eachHighCase);
}
console.log(arrayOfHighestCases);
return arrayOfHighestCases;
});
};
highestCasesData();
Attempting to make an inquiry which depends on input esteem. I am utilizing countries rest programming interface. The wished yield is the parsed information from API which is templated by handlebars markup. It would be ideal if you clarify in what capacity can fix my code. Much obliged to you.
import markupAdd from "../templates/markup.hbs";
const divInfo = document.querySelector("#main-container");
const search_input = document.querySelector(".input-field");
let search_term = "";
let countries;
const fetchCountries = () => {
countries = fetch(
"https://restcountries.eu/rest/v2/all?fields=name;flag;capital;population;languages"
).then((res) => res.json());
};
const showCountries = () => {
divInfo.innerHTML = "";
fetchCountries();
countries
.filter((country) =>
country.name.toLowerCase().includes(search_term.toLowerCase())
)
.map((item) => markupAdd(item))
.join("");
divInfo.insertAdjacentHTML("beforeend", infoBlock);
};
search_input.addEventListener("input", (e) => {
search_term = e.target.value;
showCountries();
});
handlebars
<div id="country-container">
<p class="country">{{name}}</p>
<img src="{{flag}}" alt="{{name}}" width="600" height="400">
<div id="info-container">
<p class="capital">Capital: {{capital}}</p>
<p class="population">Population: {{population}} </p>
<ul class="langs">
{{#each languages}}
<li class="language">Languages: {{name}}</li>
{{/each}}
</ul>
</div>
</div>
At the present time, after inputed any letter I am getting this kind of error
apiInfo.js?b765:22 Uncaught TypeError: countries.filter is not a function
at showCountries (apiInfo.js?b765:22)
at HTMLInputElement.eval (apiInfo.js?b765:28)
The fetchCounries function is not returning anything, one approch to solve the issue will be following.
Convert the Function to the async function
and then return the data your will get.
const fetchCountries = async () => {
let countries = await fetch(
"https://restcountries.eu/rest/v2/all?fields=name;flag;capital;population;languages"
);
let country = await countries.json();
return country;
};
const showCountries = () => {
divInfo.innerHTML = "";
fetchCountries().then(countries =>{
countries
.filter((country) =>
country.name.toLowerCase().includes(search_term.toLowerCase())
)
.map((item) => markupAdd(item))
.join("");
divInfo.insertAdjacentHTML("beforeend", infoBlock);
}).catch(err => {
console.log(err)
})
};
Async Function also returns a promise so later you can handle this using then catch block
to do it without the async await and do it more clear, you can do something like this
const fetchCountries = () => {
fetch(
"https://restcountries.eu/rest/v2/all?fields=name;flag;capital;population;languages"
)
.then((res) => res.json())
.then((data) => {
showCountries(data);
})
.catch((err) => {
console.log(err);
});
};
const showCountries = (countries) => {
divInfo.innerHTML = "";
countries
.filter((country) =>
country.name.toLowerCase().includes(search_term.toLowerCase())
)
.map((item) => markupAdd(item))
.join("");
divInfo.insertAdjacentHTML("beforeend", infoBlock);
};
Change your function like this :
async function fetchCountries() {
response = await fetch ("https://restcountries.eu/rest/v2/all?fields=name;flag;capital;population;languages");
return await response.json();
};
And where you are calling the function , just use .then to get the data.
fetchCountries().then().catch();
I've a very simple script that gets me some info by mapping over an array of around 150 records and the code seems to work fine with smaller number of records but everytime I run it with this 150 records it just stops working and doesn't continue and I think it might be a Promise.all problem.
any idea?
code:
const request = require('request');
const axios = require('axios');
const cheerio = require('cheerio');
const fs = require('fs').promises;
let champions = [];
const getChampData = async hrefs => {
const requests = hrefs.map(async ({ href }) => {
try {
const html = await axios.get(href);
const $ = cheerio.load(html.data);
const champName = $('.style__Title-sc-14gxj1e-3 span').text();
let skins = [];
$('.style__CarouselItemText-sc-1tlyqoa-16').each((_, el) => {
const skinName = $(el).text();
skins.push(skinName);
});
const champion = {
champName,
skins
};
console.log(champion);
return champion;
} catch (err) {
console.error(err);
}
});
const results = await Promise.all(requests);
await fs.writeFile('json/champions-skins.json', JSON.stringify(results));
return results;
};
edit #1:
I used a package called p-map with it and now everything works just fine!
const axios = require('axios');
const pMap = require('p-map');
const cheerio = require('cheerio');
const fs = require('fs').promises;
const getChampData = async hrefs => {
// const champions = JSON.parse(await fs.readFile('json/champions.json'));
try {
let champsList = await pMap(hrefs, async ({ href }) => {
const { data } = await axios(href);
const $ = cheerio.load(data);
const champName = $('.style__Title-sc-14gxj1e-3 span').text();
let skins = [];
$('.style__CarouselItemText-sc-1tlyqoa-16').each((_, el) => {
const skinName = $(el).text();
skins.push(skinName);
});
const champion = {
champName,
skins
};
console.log(champion);
return champion;
});
await fs.writeFile(
'champions-with-skins-list.json',
JSON.stringify(champsList)
);
} catch (err) {
console.error(err.message);
}
};
On Error return is missing. Look like issue with some url to fetch.
const getChampData = async hrefs => {
const requests = hrefs.map(async ({ href }) => {
try {
const html = await axios.get(href);
// rest of the code
} catch (err) {
console.error(err);
return []
}
});
const results = await Promise.all(requests);
await fs.writeFile("json/champions-skins.json", JSON.stringify(results));
return results;
};