Build a Weather App - Build a Weather App

Tell us what’s happening:

I am failing the final test:

When Paris is selected the app should show an alert with Something went wrong, please try again later

As far as I can tell, my app is behaving as expected so I’m a little confused at what I’m doing wrong?

Your code so far

<!-- file: index.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link href="./styles.css" rel="stylesheet"/>
    <title>Weather App</title>
  </head>

  <body>
    <div id="city-select">
      <select id="city-choice">
        <option value=""></option>
        <option value="new york">New York</option>
        <option value="los angeles">Los Angeles</option>
        <option value="chicago">Chicago</option>
        <option value="paris">Paris</option>
        <option value="tokyo">Tokyo</option>
        <option value="london">London</option>
      </select>
      <button id="get-forecast">Get Forecast</button>
    </div>
    <div id="weather-display">
      <h1 id="location"></h1>
      <div class="main">
        <div class="vertical-align">Temperature: <span id="main-temperature"></span>°C</div>
        <div>
          <span id="weather-main" class="vertical-align"></span>
          <img id="weather-icon">
        </div>
      </div>
      <p>Feels like: <span id="feels-like"></span>°C</p>
      <p>Humidity: <span id="humidity"></span>%</p>
      <p>Wind speed: <span id="wind"></span> mps</p>
      <p>Gust: <span id="wind-gust"></span> mps</p>
    </div>
    
    <script src="./script.js"></script>
  </body>
</html>
/* file: styles.css */
*, *::before, *::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

body {
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  font-family: Robota, sans-serif;
  font-size: 24px;
}

#city-select {
  display: block;
  margin: auto;
  margin-top: 5%;
  width: 50%;
}

#city-choice {
  width: 50%;
  font-size: 1.5rem;
}

#get-forecast {
  width: 40%;
  padding: 1%;
  font-size: 1.2rem;
}

#weather-display {
  display: none;
  margin: auto;
  margin-top: 2%;
  padding: 2% 5%;
  height: 400px;
  width: 800px;
  border: 3px solid skyblue;
}

h1 {
  text-align: center;
  margin-bottom: 3%;
}

.main {
  display: flex;
  justify-content: space-around;
  align-items: center;
  font-size: 1.75rem;
}

.vertical-align {
  height: 2rem;
  line-height: 2rem;
}

img {
  height: 100px;
  width: 100px;
  display: inline-block;
  float: right;
}




/* file: script.js */
const getForecastBtn = document.getElementById("get-forecast");
const cityChoice = document.getElementById("city-choice");

const getWeather = async (city) => {
  try {
  const res = await fetch(`https://weather-proxy.freecodecamp.rocks/api/city/${city}`);
  const data = await res.json();
  return data?.error ? alert("Something went wrong, please try again later") : data
  } catch (e) {
    console.log(e);
  }
}

const showWeather = async (city) => {
  if (!city) return;
  const data = await getWeather(city);
  const { main, icon } = data.weather[0];
  const { temp, feels_like, humidity } = data.main;
  const { speed, gust } = data.wind;
  let cityFormatted = city.split(' ').map(e=>e[0].toUpperCase() + e.slice(1)).join(' ');
  const displayElements = [
    [document.getElementById("location"), cityFormatted],
    [document.getElementById("main-temperature"), temp],
    [document.getElementById("weather-main"), main],
    [document.getElementById("feels-like"), feels_like],
    [document.getElementById("humidity"), humidity],
    [document.getElementById("wind"), speed],
    [document.getElementById("wind-gust"), gust]
  ];
  displayElements.forEach(element=>element[0].innerText = element[1] || "N/A")
  document.getElementById("weather-icon").src = icon; 
};

getForecastBtn.addEventListener("click", () => {
  document.getElementById("weather-display").style.display = "block";
  showWeather(cityChoice.value);
  });

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36

Challenge Information:

Build a Weather App - Build a Weather App

I am absolutely confused by what’s going on there.

To test the alert we usually overwrite it to add the message to an array, but it seems that it is not being overwritten and so the tests are failing because the array being tested is empty

I am going to do a local build to test more

oh, you need to deal with this error

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘weather’)

it happens with paris being selected

  const data = await getWeather(city);
  const { main, icon } = data.weather[0];

Thanks for your help, though I’m still stuck.
My getWeather function returns the specified alert if there is no weather data for the given city (e.g. ‘paris’).
Meanwhile, the value of data in showWeather will be undefined in this case.
So I tried if (!data) return immediately after the first line below.

const data = await getWeather(city);
const { main, icon } = data.weather[0];

I also tried the opposite (i.e. wrapping remaining function code in a if (data) clause).

I’m obviously not handling the ‘paris’ case correctly but I’m unclear how else to do so?

can you share your updated code?

I am trying to compare with the test, I have no idea why it fails

Here’s my updated script:

const getForecastBtn = document.getElementById("get-forecast");
const cityChoice = document.getElementById("city-choice");

const getWeather = async (city) => {
  try {
  const res = await fetch(`https://weather-proxy.freecodecamp.rocks/api/city/${city}`);
  const data = await res.json();
  return data?.error ? alert("Something went wrong, please try again later") : data;
  } catch (e) {
    console.log(e);
  }
}

const showWeather = async (city) => {
  if (!city) return ;
  const data = await getWeather(city);
  if (!data) return;
  document.getElementById("weather-display").style.display = "block";
  const { main, icon } = data?.weather[0];
  const { temp, feels_like, humidity } = data.main;
  const { speed, gust } = data.wind;
  let cityFormatted = city.split(' ').map(e=>e[0].toUpperCase() + e.slice(1)).join(' ');
  const displayElements = [
    [document.getElementById("location"), cityFormatted],
    [document.getElementById("main-temperature"), temp],
    [document.getElementById("weather-main"), main],
    [document.getElementById("feels-like"), feels_like],
    [document.getElementById("humidity"), humidity],
    [document.getElementById("wind"), speed],
    [document.getElementById("wind-gust"), gust]
  ];
  displayElements.forEach(element=>element[0].innerText = element[1] || "N/A")
  document.getElementById("weather-icon").src = icon; 

};

getForecastBtn.addEventListener("click", () => {
  showWeather(cityChoice.value);
});

What happens to your getWeather function when data === undefined?

Additionally, what could you do to ensure the validity of res before calling json()?

Welcome back @KingofRedOnions!

As I understand it, any error thrown by the fetch request in getWeather() will be caught by try ... catch.
If the route https://weather-proxy.freecodecamp.rocks/api/city/${city} is not found, res.json() will be an object with an error property, which triggers an alert.

It’s only in showWeather that data would be undefined in this case, in which case I return early and avoid trying to access data properties which don’t exist.

It would be helpful if someone could tell me the easiest way to look at exactly what the FCC tests are doing, as I really should know how to do that?

Hi @igorgetmeabrain

Can you try entering paris as the endpoint for the API into a browser?
What data if any is retrieved?

Happy coding

Yes, whenever I enter ‘paris’ (or any other route for which there is no data), res.json() is {"error":"Weather information for city '[city]' not found."}, so I handle that with the alert:

return data?.error ? alert("Something went wrong, please try again later") : data;

I solved it. I handled error/alert in showWeather instead of getWeather (with optional chaining).

Thanks for your help everyone!