So I'm trying to implement a search bar in my Flask application that will list out the cities that are being inputted by the user and exist in the JSON API results of a weather API.
I am following a tutorial and basically have the same code as ths: https://codepen.io/jamesqquick/pen/XWJxBQv
However, in my implementation, the .filter() and .map() functions don't work, I get the following error: TypeError for map() and filter()
How do I fix this?
Here's my code (the regular generateHTML in the first part of the code with fetching current weather data already works, only the "SEARCH BAR" section has problems):
let currentType = "current.json";
let userCity = "London";
const apiData = {
url: "http://api.weatherapi.com/v1",
type: `${currentType}`,
key: "40cd513af8aa446484a92837213011",
city: `${userCity}`,
};
const { url, type, key, city } = apiData;
const apiUrl = `${url}/${type}?key=${key}&q=${city}`;
console.log("apiUrl:");
console.log(apiUrl);
fetch(apiUrl)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Response not ok.");
})
.then((locationRequest) => generateHtml(locationRequest))
.catch((error) => console.error("Error:", error));
const generateHtml = (data) => {
console.log("data:")
console.log(data);
console.log("data.location.name:")
console.log(`${data.location.name}`);
const html = `
<div >
<h1>${data.location.name}, ${data.location.country}</h1></div>
<div >
<span>Tmp: ${data.current.temp_c} °C</span>
<span>Feels like: ${data.current.feelslike_c} °C</span>
</div>
`;
const weatherDiv = document.querySelector(".weather");
weatherDiv.innerHTML = html;
};
/* SEARCH BAR */
const citiesList = document.getElementById('weather-cities');
const searchBar = document.getElementById('weather-searchbar');
let cities = [];
console.log("citiesList:");
console.log(citiesList);
console.log("searchBar:");
console.log(searchBar);
searchBar.addEventListener('keyup', (e) => {
userCity = e.target.value.toLowerCase();
console.log("usercity:");
console.log(userCity);
const filteredCities = cities.filter((city) => {
return (
city.name.toLowerCase().includes(userCity) ||
city.region.toLowerCase().includes(userCity) ||
city.country.toLowerCase().includes(userCity)
);
});
displayCities(filteredCities);
});
const loadCities = async () => {
try {
currentType = "search.json";
const res = await fetch(apiUrl);
cities = await res.json();
console.log("cities:");
console.log(cities);
displayCities(cities);
} catch (err) {
console.error(err);
}
};
const displayCities = (cities) => {
let htmlString = cities
.map((city) => {
return `
<li >
<h2>${city.location.name}</h2>
<p>Temperature: ${city.current.temp_c} °C</p>
<p>Feels like:${city.current.feelslike_c} °C></p>
</li>
`;
})
.join('');
citiesList.innerHTML = htmlString;
};
loadCities();
<div class="other-stats">
<div class="weather-search">
<input type="text" id="weather-searchbar" placeholder="Your city"></input>
<ul id="weather-cities"></ul>
</div>
<div class="weather"></div>
</div>
<script src="../static/weather_api.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Array.prototype.filter() and Array.prototype.map() are used for arrays, the cities property is getting a javascript object. You need to assign an array to "cities".
CodePudding user response:
Ok, it seems I solved the issue for now. In displayCities, for the HTML section I put city.location.name
like I'd do to get the name in "current.json" API call, but in the new API call "search.json" I get an array of dictionaries that contain different information directly, not with different categories like "location" or "current". So city.name
is enough. To clarify better, see console.log entries:
const displayCities = (cities) => {
let htmlString = cities
.map((city) => {
return `
<li >
<p>${city.name}</p>
</li>
`;
})
.join('');
citiesList.innerHTML = htmlString;
};