I am trying to make a site for practice that gets the movies from the OMDB api and displays the HTML on the page once the user searches for a term.
The problem is that the user needs to click the search button twice for the html to appear.
The api responds and populates my Array (fullMovies) but the html (movieItemHTML function) does not get displayed on the first click but on the second
I am guessing it has something to do with async await but for the past 3 hours I can not figure it out. Any help appreciate it!
const form = document.getElementById('form');
const movieItem = document.querySelector('.movies');
const APIKEY = 'a3f1f778';
const exploring = document.querySelector('.exploring');
const searchInput = document.getElementById('query');
const movies = [];
const fullMovies = [];
form.addEventListener('submit', (e) => {
e.preventDefault();
getMovies();
// console.log(fullMovies);
});
const getMovies = async () => {
const url = `http://www.omdbapi.com/?s=${searchInput.value}&apikey=${APIKEY}`;
const res = await fetch(url);
const movieData = await res.json();
movieData.Search.forEach((e) => movies.push(e));
searchMovieDetails();
};
const searchMovieDetails = () => {
movies.forEach(async (e) => {
const res = await fetch(
`http://www.omdbapi.com/?t=${e.Title}&apikey=${APIKEY}`
);
const data = await res.json();
fullMovies.push(data);
});
movieItemHTML();
};
function movieItemHTML() {
exploring.style.display = 'none';
let html = '';
fullMovies.forEach((e) => {
html = `
<div class="movie-item">
<img class="poster" src=${e.Poster}/>
<div class="movie-desc">
<div class="title-rating">
<h3>${e.Title}</h3>
<img src="./images/star.png" class="star"/>
<p class="rating"> ${e.imdbRating}</p>
</div>
<div class="time-kind-btn">
<p>${e.Runtime}</p>
<p>${e.Genre}</p>
<a class="watchlist"><img src="./images/addBtn.png"> Watchlist</a>
</div>
<div>
<p class="plot">${e.Plot}</p>
</div>
</div>
</div>
`;
movieItem.innerHTML += html;
});
}
It happens because the searchMovieDetails()
function invokes movieItemHTML()
function synchronously even before the fullMovies
array gets populated. And sorry but this is a terrible code on many levels. Primiarily the side effects deceive you to believe that you get them on the second click.
Stay away from the async functions unless you really need them if you ever do. Asynchronous operations shouldn't be implemented as if they are synchronous imperative code. This is a wart of JS.
You can use a single function for this job.
function getMovies(searchInput, APIKEY){
fetch(`http://www.omdbapi.com/?s=${searchInput.value}&apikey=${APIKEY}`)
.then(r => r.json())
.then(j => Promise.all(j.Search.map(m => fetch(`http://www.omdbapi.com/?t=${m.Title}&apikey=${APIKEY}`)
.then(r => r.json())
)
)
)
.then(movies => movies.forEach(appendMovieToWhateverDOMElement))
.catch(handleError);
}