Wanderlust Challenge - Help fetching venue photo

Greetings everyone,

I’m having a hard time finishing the bonus for this project:

"
For a real challenge, try fetching venue photos! This will require an additional request for venue details for each venue, as the photo information is not returned in the initial request.
"
https://www.codecademy.com/paths/web-development/tracks/webdev-intermediate-javascript/modules/intermediate-javascript-requests/projects/wanderlust

Check my code bellow:

// Foursquare API Info
const clientId = '#';

const clientSecret = '#';

const url = (...)

const urlPhoto = (...)

// APIXU Info
const apiKey = '#';

const forecastUrl = (...)


// Page Elements
const $input = $('#city');
const $submit = $('#button');
const $destination = $('#destination');
const $container = $('.container');
const $venueDivs = [$("#venue1"), $("#venue2"), $("#venue3"), $("#venue4"), $("#venue5")];
const $weatherDivs = [$("#weather1"), $("#weather2"), $("#weather3"), $("#weather4")];
const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
let today = new Date().toJSON().slice(0,10).replace(/-/g,'');
let arrIds = [];

//&${}
// Add AJAX functions here:
const getVenues = async () => {
  const city = $input.val();
  const urlToFetch = `${url}${city}&limit=3&client_id=${clientId}&client_secret=${clientSecret}&v=${today}`;
 
try {
  	const response = await fetch(urlToFetch)
    	
    if(response.ok) {
      // console.log(response)  
    	const jsonResponse = await response.json();
      const venues = jsonResponse.response.groups[0].items.map(item => {

	return item.venue
                
});
      return venues
  	}
    
	} catch (error) {
    console.log(error);  
  }
};

const getPhotos = async (venueId) => {
  const urlToFetch = `https://api.foursquare.com/v2/venues/${venueId}/photos?&client_id=${clientId}&client_secret=${clientSecret}&v=${today}`;
  
  try {
    const response = await fetch(urlToFetch)
    if(respose.ok) {
      const jsonResponse = await response.json();
      
      let urlPhoto = `https://fastly.4sqi.net/img/general/200x200${jsonResponse.response.photos.items[0].suffix}`;
     
      return urlPhoto;

    }
  } catch (error) {
    console.log(error)
  }
}

//&${}
const getForecast = async () => {
  const userInput = $input.val();
	const urlToFetch = `${forecastUrl}${apiKey}&q=${userInput}&days=4&hour=11`;
  
  try {
		const response = await fetch(urlToFetch)
    if(response.ok) {
       const jsonResponse = await response.json();
      const days = jsonResponse.forecast.forecastday;    
      
    return days;
    }
  } catch (error) {
    console.log(error);
  }
};

// Render functions

const renderVenues = (venues) => {
  
  $venueDivs.forEach(($venue, index) => {
    
  	let randomIndex = Math.floor(Math.random() * venues.length)
    
		const venue = venues[randomIndex]
    const venueIcon = venue.categories[0].icon;
    const venueImgSrc = `${venueIcon.prefix}bg_64${venueIcon.suffix}`
    // const venueId = venue.id;
		// venues[index].id
    
    let photos = getPhotos(venue.id)
    
    let venueContent = createVenueHTML(venue.name, venue.location, venueImgSrc, photos);
    $venue.append(venueContent);
    
  });
  $destination.append(`<h2>${venues[0].location.city}</h2>`);
}

const renderForecast = (days) => {
  // console.log(days)
  $weatherDivs.forEach(($day, index) => {
    const currentDay = days[index]
    
    let weatherContent = createWeatherHTML(currentDay);
    $day.append(weatherContent);
  });
}

const executeSearch = () => {
  $venueDivs.forEach(venue => venue.empty());
  $weatherDivs.forEach(day => day.empty());
  $destination.empty();
  $container.css("visibility", "visible");
  getVenues()
    .then((venues) => {
    	return renderVenues(venues)
  })
  getForecast().then((forecast) => {
    	return renderForecast(forecast)
	})
  return false;
}


$submit.click(executeSearch)

I would greatly appreciate if someone could guide me through this exercise and help me retrieve those pictures from Foursquare API. I’m anxious to make it work! haha. :grin:

This is my first post in the community. Any feedback to improve future posts and make it easier will be appreciated. :smiley:

Thank you again! :upside_down_face:

1 Like

Hi, and welcome. Do you mind using the “Preformatted text” option for all your code as a whole?

1 Like

There. Much better indeed. Any suggestions to get this exercise done ?

1 Like

I’ve tried debugging this with https://playcode.io/
Never used async, wait and fetch myself, looking very interesting :open_mouth:

Apparently, using the arrow function syntax you’re missing a fat arrow here, I’ve marked it with * *
const getVenues = async *=>* () => {

After that it’s telling me you’re trying to use await outside of async here (maybe I’m making a mistake here?):

const getVenues = async => () => {
  const city = $input.val();
  const urlToFetch = `${url}${city}&limit=3&client_id=${clientId}&client_secret=${clientSecret}&v=${today}`;
 
try {
  	const response = *await* fetch(urlToFetch);
    	
    if(response.ok) {
      // console.log(response)  
    	const jsonResponse = await response.json();
      const venues = jsonResponse.response.groups[0].items.map(item => {

	return item.venue;
                
});

Marked again with * * I guess that’s your clue :slight_smile:

1 Like

Hi @coderighton,

adding another arrow function will result in incorrect syntax in that case. As for the await we can only use await inside an async function and that’s what is being done there.

When I remove my getPhotos async function all of the other functions work properly. I just can’t seem to find a way to get the photos and render to the page.

Thank you for your kind help. :slightly_smiling_face:

1 Like

Is there anybody there who could help me with this ?

The only syntax shown on async: MDN all uses,

async function()

Have you got documentation that shows it will work with arrow functions, as well? If not, maybe adopt this syntax and see what happens.

1 Like

The arrow functions were actually the boilerplate of this exercise. The functions work fine. My problem is getting the photos from API. I don’t know how to do it. :neutral_face:

Is the query operator in url?

example.com/...?city=
${apiKey}?q=${userInput}...

Yes. I get back the data from the API I just can’t figure out how to render to the page. I’m gonna take a few screenshots and post it here. It seems like my question is very vague and some people can’t understand exactly what I need help with. I will be more specific.

This did the trick for me (based on ruby_mcpython’s post):

First, get the photos from Foursquare’s API:

> const getPhotos = async (venueId) => {
> 		const url2ToFetch = url2 + venueId + '?client_id=' + clientId + '&client_secret=' + clientSecret + '&v=20190416';
>   try {
>     const response = await fetch(url2ToFetch);
>     if (response.ok) {
>       const jsonResponse = await response.json();
>       const photos = jsonResponse.response.venue.photos.groups[1].items[0].prefix + 'width960' + jsonResponse.response.venue.photos.groups[1].items[0].suffix;
>       return photos;
>     }
>   }
>   catch(err){console.log(err)}

Then, rewrite the renderVenues function’s forEach to be async and get the photos to render:

> // Render functions
> const renderVenues = (venues) => {
>   $venueDivs.forEach(async ($venue) => {
>     let index = Math.floor(Math.random()*10)
>     // Add your code here:
> 	const venue = venues[index];
>     let photo = await getPhotos(venue.id);
>     const venueIcon = venue.categories[0].icon;
>     const venueImgSrc = venueIcon.prefix + 'bg_64' + venueIcon.suffix;
>     let venueContent = createVenueHTML (venue.name, venue.categories[0], venue.location, photo);
>     $venue.append(venueContent);
>   });
>   $destination.append(`<h2>${venues[0].location.city}</h2>`);
> }

No code changes required in the executeSearch function at the bottom.

2 Likes

@marlondb Your code was so close! Inside your renderVenues function when you call getPhotos a promise is being returned. To solve, we need to wait for the promise to resolve. We can do this with the await keyword or .then().

For the renderVenues function @stvn.juhasz 's solve with await works great with a few tweaks to make sure it works with your code, or we can use .then(). Also, no need to change the executeSeach function like @stvn.juhasz said.

Code for both the await and .then() solves below:

// Render functions

//render venues/photos using await
/* const renderVenues = (venues) => {
    $venueDivs.forEach(async ($venue) => {
        let index = Math.floor(Math.random() * 10)
        // Add your code here:
        const venue = venues[index];
        const venueIcon = venue.categories[0].icon;
        const venueImgSrc = `${venueIcon.prefix}bg_64${venueIcon.suffix}`;
        let photo = await getPhotos(venue.id);
        let venueContent = createVenueHTML(venue.name, venue.location, venueImgSrc, photo);
        $venue.append(venueContent);
    });
    $destination.append(`<h2>${venues[0].location.city}</h2>`);
} */

//render venues/photos using .then()
/* const renderVenues = (venues) => {
    $venueDivs.forEach(($venue, index) => {
        // Add your code here:
        const venue = venues[index];
        const venueIcon = venue.categories[0].icon;
        const venueImgSrc = `${venueIcon.prefix}bg_64${venueIcon.suffix}`;
        let photo = getPhotos(venue.id).then(url => {
            let venueContent = createVenueHTML(venue.name, venue.location, venueImgSrc, url);
            $venue.append(venueContent);
        });
    });
    $destination.append(`<h2>${venues[0].location.city}</h2>`);
}*/

With our renderVenues function working as expected to get the photo’s URL we need to be sure our helper function, createVenueHTML, is accounting for this new data and that it will render an image. createVenueHTML in helper.js should look something like this:

const createVenueHTML = (name, location, iconSource, photoUrl) => {
  return `<h2>${name}</h2>
  <img class="venueimage" src="${iconSource}"/>
  <h3>Address:</h3>
  <p>${location.address}</p>
  <p>${location.city}</p>
  <p>${location.country}</p>
  <img alt="" src=${photoUrl} />`;
}

Also, great job with the getPhotos function! Line 42 (in code you shared above) has a slight typo in if(response.ok) statement. if(respose.ok) should be if(response.ok) Be sure to fix this!

2 Likes

Hey there, i’ve got stuck with the same challenge, can’t solve to fetch additional details (photos) to the venues. Here is a link to my code to check it. Thx.

https://gist.github.com/730ff2ecbdece6adee944f9b7986831b

Print screen of errors is here:

Hey guys,

please check my Wanderlust with the challenge complete. I think it’s working.
https://candidemile.github.io/

@serge2serge i have the same issue. I think foursquare has a limit on number of requests. When i tried again on the next day it worked for while without errors.

p.s. https://developer.foursquare.com/docs/api/troubleshooting/rate-limits

Great! Thanks a lot, i’ll check it later.

@marlondb
Hi , I have walked through this challenge , in yr getPhotos function , the urlToFetch should be
const url=‘https://api.foursquare.com/v2/venues/
const urlToFetch=${url}${venue.id}/photos?client_id=${clientId}&client_secret=${clientSecret}&v=20201208
Unfortunately, the documentation on FourSquare is not clear, Here is my getVenuePhotos 's code:
const getVenuePhotos=async (venue)=>{
const url=‘https://api.foursquare.com/v2/venues/
const urlToFetch=${url}${venue.id}/photos?client_id=${clientId}&client_secret=${clientSecret}&v=20201208
try{
const response=await fetch(urlToFetch);
if (response.ok){
const jsonResponse=await response.json();
console.log(jsonResponse);
const venuePhoto=jsonResponse.response.photos.items[0];
const photoSize=‘100x100’;
const venuePhotoSrc=${venuePhoto.prefix}${photoSize}${venuePhoto.suffix};
return venuePhotoSrc;
}

}catch(error){
console.log(error);
}
}
then you call getVenuePhotos inside the renderVenues function, something like:
getVenuePhotos(venue).then(venuePhotoSrc=>{
let venueContent = createVenueHTML(venue.name,venue.location,venueImgSrc,venuePhotoSrc);
$venue.append(venueContent);
})
Good luck