Search function error: Cannot read property 'results' of undefined - javascript

I get data from Marvel API and the data is displayed perfectly.
However, when I try to implement search function, I get this 'Uncaught TypeError: Cannot read property 'results' of undefined at displayData' error.
Here is the code:
const apiUrl = ... ;
let data = [];
const searchBar = document.querySelector("#searchbar");
searchBar.addEventListener("keyup", (e) => {
const searchString = e.target.value.toLowerCase();
const filter = data.data.results.filter((character) =>
character.name.toLowerCase().includes(searchString)
);
displayData(filter);
});
async function getData() {
const response = await fetch(apiUrl);
data = await response.json();
console.log(data);
displayData(data);
const characters = data.data.results;
console.log(characters);
}
const displayData = (data) => {
const characters = data.data.results;
console.log(characters);
for (character of characters) {
document.getElementById("output").innerHTML += `
<div class="character">
<h1>${character.name}</h1>
<a href="${character.urls[0].url}" target="_blank">
<img src="${character.thumbnail.path}.${character.thumbnail.extension}" alt="${character.name}" height="100" class="image" />
</a>
</div>
`;
}
};
These console.log show that I get the data that I want, there is no error until I try to search and then it shows me that the error is on line 26 which is 'const characters = data.data.results;' in displayData function. Any help?

When you call displayData(data) from searchbar event listener, you are passing only a filtered array (without data property), and parameter name data is shadowing global data variable, but when you call displayData from getData function, you are passing the whole data object.
You can try changing the parameter passed to displayData function, and change a call to it from getData function:
const apiUrl = ... ;
let data = [];
const searchBar = document.querySelector("#searchbar");
searchBar.addEventListener("keyup", (e) => {
const searchString = e.target.value.toLowerCase();
const filter = data.data.results.filter((character) =>
character.name.toLowerCase().includes(searchString)
);
displayData(filter);
});
async function getData() {
const response = await fetch(apiUrl);
data = await response.json();
console.log(data);
displayData(data.data.results);
const characters = data.data.results;
console.log(characters);
}
const displayData = (characters) => {
let filteredHTML = '';
for (character of characters) {
filteredHTML += `
<div class="character">
<h1>${character.name}</h1>
<a href="${character.urls[0].url}" target="_blank">
<img src="${character.thumbnail.path}.${character.thumbnail.extension}" alt="${character.name}" height="100" class="image" />
</a>
</div>
`;
};
document.getElementById("output").innerHTML = filteredHTML;
};

Related

objectobject returning with async await

Guys i have this problem with my code
`
get data
<script>
let dataBtn;
async function fetchData() {
const response = await fetch("./prodotti.json");
return await response.json();
}
const container = document.querySelector(".container");
const btn = document.querySelector("#get");
const newDiv = document.createElement("div");
btn.addEventListener("click", async () => {
const data = await fetchData();
//console.log(data);
container.appendChild(newDiv).innerHTML = data.name;
});
</script>
`
when i click on the button appears [object object] instead of the json data
It looks like you get a problem with serialisation of an object what you received from your api. Make sure the data.name is really a string and accessible.Below a working example with another example api to show that your code will work.
let dataBtn;
async function fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
return await response.json();
}
const container = document.querySelector(".container");
const btn = document.querySelector("#btnGet");
const newDiv = document.createElement("div");
btn.addEventListener("click", async () => {
const data = await fetchData();
console.log(data);
container.appendChild(newDiv).innerHTML = data.title;
});
<div class="container"></div>
<button id="btnGet">GET</button>
Try to write code like this: data[0]

JavaScript Array.filter() and .map() don't work, uncaught TypeError for Search function

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 class="weather-location">
<h1>${data.location.name}, ${data.location.country}</h1></div>
<div class="details">
<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 class="character">
<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>
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".
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:
API call "current.json"
API call "search.json"
const displayCities = (cities) => {
let htmlString = cities
.map((city) => {
return `
<li class="character">
<p>${city.name}</p>
</li>
`;
})
.join('');
citiesList.innerHTML = htmlString;
};

How to fetch the next set of results from a paginated API?

I'm fetching data from an API that is paginated server-side. I have limited the number of results to 5 (rows=5). For the first set of data, a global variable pageNumber has been declared to 1, and eventListeners for the Previous/Next buttons have been added. Now I don't know how to get the next set of results. They can be fetched by changing the pageNumber to 2 but I don't know how to access the URL from const endpoint where I would change the pageNumber parameters to get previous and/or next results. Any idea how to do that?
// First set of fetched data starts with page 1
let pageNumber = 1;
// 1. Define endpoint, fetch response and return data promise
const search = async (term) => {
const key = 'aroplosuitin';
const endpoint = `https://api.europeana.eu/record/v2/search.json`,
query = `?wskey=${key}&query=${term}&start=${pageNumber}&rows=5&profile=rich'`;
const response = await fetch(endpoint + query);
// Check response status:
if (response.status !== 200) {
throw new Error('Cannot fetch data. Response status is not 200.');
}
const data = await response.json();
return data;
};
// 2. Call search and return data promise
const searchEuropeana = async (term) => {
const data = await search(term);
return data;
};
// 3. Grab the input and invoke callback to update the UI
const searchForm = document.querySelector('#search-form');
searchForm.addEventListener('submit', (e) => {
e.preventDefault();
// grab user input
const userInput = searchForm.search.value.trim();
// reset form on submit
searchForm.reset();
// For errors
const errorOutput = document.querySelector('.error');
// Invoke searchEuropeana
searchEuropeana(userInput)
.then((data) => {
updateUI(data);
console.log(data);
})
.catch((error) => {
console.log('An error occured:', error),
(errorOutput.innerText = 'Check your spelling or network.');
});
});
// 4. Update the UI with HTML template
const updateUI = (data) => {
console.log(data);
};
// 5. Previous / Next results
const previousBtn = document.querySelector('#previousBtn'),
nextBtn = document.querySelector('#nextBtn');
previousBtn.addEventListener('click', () => {
if (pageNumber > 1) {
pageNumber--;
} else {
return;
}
console.log(pageNumber);
searchEuropeana();
});
nextBtn.addEventListener('click', () => {
pageNumber++;
console.log(pageNumber);
searchEuropeana();
});
<main id="main">
<h2>(Be)Heading</h2>
<br>
<section id="search">
<form id="search-form">
<div class="form-group search-group">
<input type="text" name="search" id="search" required>
<button id="searchButton" class="btn" type="submit">Search</button>
</div>
</form>
</section>
<br>
<section id="output">
<!-- Error messages -->
<div class="error"></div>
</section>
<button id="previousBtn" class="btn" type="submit">Previous</button>
<button id="nextBtn" class="btn" type="submit">Next</button>
</main>
// First set of fetched data starts with page 1
let pageNumber = 1;
let term = '';
// 1. Define endpoint, fetch response and return data promise
const search = async () => {
const key = 'aroplosuitin';
const endpoint = `https://api.europeana.eu/record/v2/search.json`,
query = `?wskey=${key}&query=${term}&start=${pageNumber}&rows=5&profile=rich'`;
const response = await fetch(endpoint + query);
// Check response status:
if (response.status !== 200) {
throw new Error('Cannot fetch data. Response status is not 200.');
}
const data = await response.json();
return data;
};
// 2. Call search and return data promise
const searchEuropeana = async () => {
const data = await search();
return data;
};
// 3. Grab the input and invoke callback to update the UI
const searchForm = document.querySelector('#search-form');
searchForm.addEventListener('submit', (e) => {
e.preventDefault();
// grab user input
term = searchForm.search.value.trim();
// reset form on submit
searchForm.reset();
// For errors
const errorOutput = document.querySelector('.error');
// Invoke searchEuropeana
searchEuropeana()
.then((data) => {
updateUI(data);
console.log(data);
})
.catch((error) => {
console.log('An error occured:', error),
(errorOutput.innerText = 'Check your spelling or network.');
});
});
// 4. Update the UI with HTML template
const updateUI = (data) => {
console.log(data);
};
// 5. Previous / Next results
const previousBtn = document.querySelector('#previousBtn'),
nextBtn = document.querySelector('#nextBtn');
previousBtn.addEventListener('click', () => {
if (pageNumber > 1) {
pageNumber--;
} else {
return;
}
console.log(pageNumber);
searchEuropeana();
});
nextBtn.addEventListener('click', () => {
pageNumber++;
console.log(pageNumber);
searchEuropeana();
});
EDIT:
Take a look at a more readable approach
// First set of fetched data starts with page 1
const searchFormEl = document.querySelector("#search-form");
const errorEl = document.querySelector(".query");
const nextBtn = document.querySelector("#nextBtn");
const prevBtn = document.querySelector("#previousBtn");
const searchEl = document.querySelector("#search");
let pageNumber = 1;
const getApiUrl = () => {
const key = "aroplosuitin";
const endPoint = `https://api.europeana.eu/record/v2/search.json`;
const query = `?wskey=${key}&query=${searchEl.value.trim()}&start=${pageNumber}&rows=5&profile=rich'`;
return `${endPoint}${query}`;
};
// 1. Define endpoint, fetch response and return data promise
const search = async () => {
const response = await fetch(getApiUrl());
// Check response status:
if (response.status !== 200) {
throw new Error("Cannot fetch data. Response status is not 200.");
}
const data = await response.json();
return data;
};
// 2. Call search and return data promise
const searchEuropeana = async () => await search();
// 3. Grab the input and invoke callback to update the UI
searchFormEl.addEventListener("submit", (e) => {
e.preventDefault();
// Invoke searchEuropeana
searchEuropeana()
.then((data) => {
updateUI(data);
console.log(data);
searchFormEl.reset();
})
.catch((error) => {
console.log("An error occured:", error);
errorEl.innerText = "Check your spelling or network.";
});
});
// 4. Update the UI with HTML template
const updateUI = (data) => {
console.log(data);
};
prevBtn.addEventListener("click", () => {
pageNumber = pageNumber > 1 ? pageNumber - 1 : pageNumber;
searchEuropeana().then(updateUI);
});
nextBtn.addEventListener("click", () => {
pageNumber++;
searchEuropeana().then(updateUI);
});

API search in javascript to return filtered list only

I am trying to make a filtered API search and only display the data(users) that has been filtered. However my search seems to return all the data and not just the data that has been filtered out. I can see the right data in the console log but can't seem to figure out how to get it to render on display.
For example, if I search for janet, I can see all the data that contains the name janet when I console log it but on display it still displays all the users. What am I doing wrong? Thanks
const UserList = document.getElementById('userList');
const searchBar = document.getElementById('searchBar');
let data = [];
searchBar.addEventListener('keyup', (e) => {
e.preventDefault();
const searchString = e.target.value;
console.log(e.target.value)
const filteredUsers = data.data.filter((user) => {
return (
user.first_name.includes(searchString) ||
user.email.includes(searchString)
);
});
console.log(filteredUsers)
displayUsers(filteredUsers);
});
const loadUsers = async () => {
try {
const res = await fetch('https://reqres.in/api/users');
data = await res.json();
displayUsers(data);
console.log(data)
} catch (err) {
console.error(err);
}
};
const displayUsers = (users) => {
const htmlString = data.data
.map((user) => {
return `
<li class="user">
<h2>${user.first_name}</h2>
</li>
`;
})
.join('');
userList.innerHTML = htmlString;
};
loadUsers();
On displayUsers function you are mapping on data.data variable. You should use users.map.
This might not be the problem, but you are defining UserList at the top and then using userList at the bottom, small typo maybe?

how to do Api fetch and display the results

i am working on a simple app that will fetch data from an Api and display it.
a have this function
const getAUserProfile = () => {
const api = 'https://randomuser.me/api/';
// make API call here
return fetch(api)
.then(response => response.json())
.then(response => displayUserPhotoAndName(response))
notify(`requesting profile data ...`);
};
this is where i try to fetch the data and pass as a parameter to the displayUserPhotoAndName function.
Now in the displayUserPhotoAndName function i try to create a statement that de-structures the data parameter and obtains the results property from it;
i tried to Create a second statement in the next line that de-structures the results variable i just created, and obtain the first item from it (it is an Array! See https://randomuser.me/api/). the de-structured array item should be declared as profile. This represents the profile data for the user gotten from the API call that i want to display in my app.
this is the displayUserPhotoAndName function
const displayUserPhotoAndName = (data) => {
if(!data) return;
// add your code here
const {results} = data;
const {profile} = results;
document.getElementById('name').innerHTML = profile[results];
clearNotice();
};
now i am trying to display the title, first name and last name with this line of code document.getElementById('name').innerHTML = profile[0].title + profile[0].first + profile[0].last;.
this is not working when i try to run it in sapio
There is no profile property in data or in the results array. So your assignment const {profile} = results; will be undefined
You could point profile at the first item in the results array. Then use the property paths for what you want to display
const displayUserPhotoAndName = (data) => {
if (!data) return;
// add your code here
const {results} = data;
const profile = results[0];
const fullName = profile.name.first + ' ' + profile.name.last;
document.getElementById('name').innerHTML = fullName
};
const getAUserProfile = () => {
const api = 'https://randomuser.me/api/';
// make API call here
return fetch(api)
.then(response => response.json())
.then(displayUserPhotoAndName)
};
getAUserProfile()
<div id="name"></div>
You need this probably:
document.getElementById('name').innerHTML = results[0].name.title + results[0].name.first + results[0].name.last;
Because the json looks like this:
{
"results":[
{
"gender":"male",
"name":{
"title":"mr",
"first":"vidar",
"last":"sjursen"
},
"location":{
"street":"bestum tverrvei 7385",
"city":"bratsberg",
"state":"sogn og fjordane",
"postcode":"8514",
"coordinates":{
"latitude":"57.7278",
"longitude":"-95.6342"
},
"timezone":{
"offset":"+7:00",
"description":"Bangkok, Hanoi, Jakarta"
}
},
"email":"vidar.sjursen#example.com",
"login":{
"uuid":"fbc411b4-f34c-497f-acff-8294ddcf8738",
"username":"whitezebra569",
"password":"shoes",
"salt":"8prWID0j",
"md5":"02ea3e887aaa140ad312905801ae2353",
"sha1":"c9a66349e68825dc02c74618aac8572fbdd01e5b",
"sha256":"8297cc85be1127223761fb80b8f554632a6a37c35d58d435293a8f6b2dca19f3"
},
"dob":{
"date":"1952-10-19T01:20:59Z",
"age":66
},
"registered":{
"date":"2011-11-12T03:06:29Z",
"age":7
},
"phone":"88795425",
"cell":"92914506",
"id":{
"name":"FN",
"value":"19105218888"
},
"picture":{
"large":"https://randomuser.me/api/portraits/men/25.jpg",
"medium":"https://randomuser.me/api/portraits/med/men/25.jpg",
"thumbnail":"https://randomuser.me/api/portraits/thumb/men/25.jpg"
},
"nat":"NO"
}
],
"info":{
"seed":"48917bf154395ac4",
"results":1,
"page":1,
"version":"1.2"
}
}
Which means, when you do this:
const {results} = data;
Then the array will be there, and the array doesn't have profile property to get it with:
const {profile} = results;
The problem is with this line of code
const {results} = data;
--> const {profile} = results;
the "{results}" will take the results property out of your response which in turn is an array.
when you try to take {profile} from results there is no such property like profile in it.
Which makes the profile variable undefined. This was the problem.
Note: The {something} variable will look for property in your object. [this case the property is something]
Hope this one helps
this is what worked for me
const displayUserPhotoAndName = (data) => {
if(!data) return;
// add your code here
const {results} = data;
const [profile] = [...results];
const fullName = profile.name.title + ' ' + profile.name.last + ' ' + profile.name.first;
document.getElementById('name').innerHTML = fullName
document.getElementById('photo').src = profile.picture.large
displayExtraUserInfo(profile);
clearNotice();
};
const {profile} = results; here profile is undefined because results is an array with no property profile.
You need to do const profile = [...results]; and then document.getElementById('name').innerHTML = profile[0].name.title + profile[0].name.first + profile[0].name.last;
fetch('https://randomuser.me/api/')
.then(res => res.json())
.then(data => {
if (!data) return;
const {
results
} = data;
const profile = [...results];
console.log(profile)
document.getElementById('name').innerHTML = profile[0].name.title + profile[0].name.first + profile[0].name.last;
}
)
<div id="name"></div>

Categories

Resources