Not printing out the forecast

48 Views Asked by At

I have been working on this code for a while now and I keep getting caught in a loophole, it is a weather app with API; here is the code:

All the links are linked correctly. The four days forecast does not show in the inneHTMl and console.log, but if I print the createWeatherCard, it prints out but without the data.

Code Snippet

const searchButton = document.getElementById("searchBtn");
const cityInput = document.getElementById("cityInput");
const weatherCardsDiv = document.getElementById("weather-cards");
const API_KEY = "d292bd950ad1111c4da438d7967f0848";

const createWeatherCard = (weatherItem) => {
  return`
    <div class="bg-slate-400 rounded p-3 card">
      <div class="border border-dashed border-2 border-slate-900 rounded p-4">
        <div class="text-center font-bold">(${weatherItem.dt_txt.split(" ")[0]})</div>
        <div class="relative mb-4">
          <img src="https://openweathermap.org/img/wn/${weatherItem.weather[0].icon}@4x.png" alt="cloud" class="w-[5rem] m-auto">
          <h4 class="text-center">Moderate Rain</h4>
        </div>
        <div class="flex justify-between mb-2">
          <h4>Temp -</h4>
          <p class="text-xl font-bold mb-px">${(weatherItem.main.temp - 273.15).toFixed(2)}*C</p>
        </div>
        <div class="flex justify-between  mb-2">
          <h4>Wind -</h4>
          <p class="text-xl font-bold mb-px">${weatherItem.wind.speed} M/S</p>
        </div>
        <div class="flex justify-between ">
          <h4>Humidity -</h4>
          <p class="text-xl font-bold mb-px">${weatherItem.item.main.humidity}%</p>
        </div>
      </div>
    </div>
  `;
}

const getWeatherDetails = (cityName, lat, lon) => {
  const WEATHER_API_URL = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${API_KEY}`;

  fetch(WEATHER_API_URL).then(res => res.json()).then(data => {
    const uniqueForecastDays = [];
    const fourDaysForecast = data.list.filter(forecast => {
      const forecastDate = new Date(forecast.dt_txt).getDate();
      if(!uniqueForecastDays.includes(forecastDate)) {
        return uniqueForecastDays.push(forecastDate);
      }

    });

    cityInput.value = "";
    weatherCardsDiv.innerHTML = "";

    console.log(fourDaysForecast);
    fourDaysForecast.forEach(weatherItem => {
      weatherCardsDiv.insertAdjacentHTML("beforeend", createWeatherCard(weatherItem));
    })
  }).catch(() => {
    // alert("An error occurred when fetching the data");
  });
}
 
const getCity = () => {
  const cityName = cityInput.value.trim();
  if(!cityName) return;
  const GEOCODING_API_URL = `http://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=1&appid=${API_KEY}`;

  fetch(GEOCODING_API_URL).then(res => res.json()).then(data => {
    if(!data.length) return alert(`No Data found for ${cityName}`);
    const { name, lat, lon} = data[0];
    getWeatherDetails(name, lat, lon); 
  }).catch(() => {
    alert("An error occurred while fetching the data")
  });
}

searchButton.addEventListener("click", getCity);

searchButton.click();
<label for="cityInput">City</label>
<input id="cityInput" type="text" value="Rome">
<input type="button" id="searchBtn" value="Search">
<div id="weather-cards"></div>

<script src="https://cdn.tailwindcss.com"></script>

1

There are 1 best solutions below

1
FiddlingAway On

The error is in your createWeatherCard function, and how you're getting the humidity info from the weatherItem.

Instead of

<p class="text-xl font-bold mb-px">${weatherItem.item.main.humidity}%</p>

do this

<p class="text-xl font-bold mb-px">${weatherItem.main.humidity}%</p>

Also, be sure to replace http with https inside your getCity() function.


Addendum (as suggested by @Yogi in the comments)

Your code was failing silently when attempting to createWeatherCard, because weatherItem.item.main.humidity does not exist. How do we know this? Firstly, the error catching would need a slight adjustment, like so:

// ... rest of the code
fetch(WEATHER_API_URL).then(res => res.json()).then(data => {
    // ... rest of the code
    fourDaysForecast.forEach(weatherItem => {
      weatherCardsDiv.insertAdjacentHTML("beforeend", createWeatherCard(weatherItem));
    })
  }).catch((err) => { // <-- this is the first addition
    console.log(err.message); // <-- this is the second addition (or you can use alert(err.message);
    // alert("An error occurred when fetching the data");
  });
// ... rest of the code

This will tell us that weatherItem.item is undefined. But is it, really? Let's log the entire weatherItem to the console right after we start looping, and check.

// ... rest of the code
fourDaysForecast.forEach(weatherItem => {
    console.log(weatherItem); // <-- this is what we added
    weatherCardsDiv.insertAdjacentHTML("beforeend", createWeatherCard(weatherItem));
});
// ... rest of the code

The browser console shows us this:

{
  "dt": 1706367600,
  "main": {
    "temp": 288.47,
    "feels_like": 287.55,
    "temp_min": 288.47,
    "temp_max": 288.96,
    "pressure": 1027,
    "sea_level": 1027,
    "grnd_level": 1023,
    "humidity": 57,
    "temp_kf": -0.49
  },
  "weather": [
    {
      "id": 800,
      "main": "Clear",
      "description": "clear sky",
      "icon": "01d"
    }
  ],
  "clouds": {
    "all": 0
  },
  "wind": {
    "speed": 0.98,
    "deg": 342,
    "gust": 1.57
  },
  "visibility": 10000,
  "pop": 0,
  "sys": {
    "pod": "d"
  },
  "dt_txt": "2024-01-27 15:00:00"
}

From there, it's obvious that we could do (as indeed you were doing):

weatherItem.dt_txt.split(" ")[0]
weatherItem.weather[0].icon
(weatherItem.main.temp - 273.15).toFixed(2)
weatherItem.wind.speed

since the keys dt_txt,weather,main.temp, and wind.speed exist within the weatherItem object.

However, there's no item key. And the one we're looking for - humidity - is not a first-level key of your weatherItem. The main key is the one holding the information about humidity. That's why the correct thing to (in your setup) was to access humidity by doing weatherItem.main.humidity.