The Code

                    
                        const API_KEY = "SECRET";

                        // Calls the API and gets the movies, async tells it to wait, returns movie array
                        async function getMovies() {
                        
                            // attempt to get all the popular movies through API
                            try {
                                let response = await fetch('https://api.themoviedb.org/3/movie/popular', {
                                    headers: {
                                        'Authorization': `Bearer ${API_KEY}`
                                    }
                                });
                        
                                // changes the string recieved back and parses it into an object
                                let data = await response.json();
                        
                                return data.results;
                        
                            // if something goes wrong, show error alert
                            } catch (error) {
                                swal.fire({
                                    icon: 'error',
                                    backdrop: false,
                                    title: 'Oops!',
                                    text: 'Something went wrong reaching the TMBD API.'
                                })
                            }
                        }
                        
                        // Calls the API and gets information for the one movie
                        async function getMovie(movieId) {
                        
                            // Attempt to get movie details through API
                            try {
                                let response = await fetch(`https://api.themoviedb.org/3/movie/${movieId}`, {
                                    headers: {
                                        'Authorization': `Bearer ${API_KEY}`
                                    }
                                });
                        
                                // changes the string recieved back and parses it into an object
                                let data = await response.json();
                        
                                return data;
                        
                            // if something goes wrong, show error alert
                            } catch (error) {
                                swal.fire({
                                    icon: 'error',
                                    backdrop: false,
                                    title: 'Oops!',
                                    text: 'Something went wrong reaching the TMBD API.'
                                })
                            }
                        }

                        // Shows all the movie cards on the page
                        function displayMovies(movies) {

                            // Get the div that the movie cards will be placed into
                            const movieListDiv = document.getElementById('movie-list');

                            // Initialize the DIV, make sure it's empty
                            movieListDiv.textContent = '';

                            // Get the template for the movie cards
                            const moviePosterTemplate = document.getElementById('movie-card-template');
                        
                            // Create the movie card from the template for each movie returned in the list
                            for (let i = 0; i < movies.length; i++) {
                        
                                let movie = movies[i];
                        
                                // Copy everything in template tags
                                let movieCard = moviePosterTemplate.content.cloneNode(true);
                        
                                // Uses a CSS selector to get the element and reassign the value of the img src attribute
                                let movieImgElement = movieCard.querySelector('.card-img-top');
                                movieImgElement.src = `https://image.tmdb.org/t/p/w500${movie.poster_path}`
                        
                                // Get the element holding the movie title and replace the text with the title of the movie
                                let movieTitleElement = movieCard.querySelector('.card-body > h5');
                                movieTitleElement.textContent = movie.title;
                        
                                // Get the element holding the paragraph information and replace the text with the summary of movie
                                let movieParagraphElement = movieCard.querySelector('.card-text');
                                movieParagraphElement.textContent = movie.overview;
                        
                                // Make up own attribute to assign movie id to access this information later for the modal
                                let movieButton = movieCard.querySelector('.btn-primary');
                                movieButton.setAttribute('data-movieId', movie.id);
                        
                                // Add the movie card to the page
                                movieListDiv.appendChild(movieCard);
                            } 
                        
                        }
                        
                        // Get the list of movies with no filter and put them on the page
                        async function displayAllMovies() {
                        
                            let movies = await getMovies();
                            displayMovies(movies);
                            
                        }
                        
                        // Put the movie details in the modal when "More Info" button is pressed
                        async function showMovieDetails(clickedBtn) {
                        
                            // get the ID of the movie that was clicked
                            let movieId = clickedBtn.getAttribute('data-movieId');
                        
                            // get the details of the movie with that ID from TMBD API
                            let movieData = await getMovie(movieId);
                        
                            // Modify Img on Modal
                           let movieImg = document.querySelector('#movieModal .movie-image');
                           movieImg.src = `https://image.tmdb.org/t/p/w500${movieData.poster_path}`
                        
                           // Modify Title on Modal
                           let modalTitle = document.querySelector('#movieModal .movie-title');
                           modalTitle.textContent = movieData.title;
                        
                           // Modify Tagline on Modal
                           let modalTagline = document.querySelector('#movieModal .tagline');
                           modalTagline.textContent = `${movieData.tagline}`;
                        
                           // Display genres on page
                           displayGenres(movieData.genres);
                        
                           // Modify overview summary of Modal
                           let modalBody = document.querySelector('#movieModal .overview');
                           modalBody.textContent = movieData.overview;
                               
                           // Modify homepage website button links to on Modal
                           let moviePageBtn = document.querySelector('#movieModal .btn-primary');
                           moviePageBtn.href = movieData.homepage;
                        
                           // Modify Release Date
                           let releaseDateMonth = document.getElementById('month');
                           releaseDateMonth.textContent = movieData.release_date.slice(5,7);
                        
                           let releaseDateDay = document.getElementById('day');
                           releaseDateDay.textContent = movieData.release_date.slice(8,10);
                        
                           let releaseDateYear = document.getElementById('year');
                           releaseDateYear.textContent = movieData.release_date.slice(0,4);
                        
                           // Modify Runtime
                           let runtime = document.getElementById('runtime');
                           runtime.textContent = movieData.runtime;
                        
                           // Modify Voter average
                           let voteAvg = document.getElementById('voteAvg');
                           voteAvg.textContent = (movieData.vote_average).toFixed(2);
                        
                           // Modify Voter count
                           let voteCount = document.getElementById('voteCount');
                           voteCount.textContent = movieData.vote_count;
                        }
                        
                        // Add genre badges to genre div on page with all Genres associated with movie
                        function displayGenres(movieGenreArray) {
                        
                            // Get div element to hold genre badges
                            const genreDiv = document.getElementById('genres');
                            genreDiv.textContent = '';
                        
                            // loop through each item in array and add genre element to div element
                            for (let i = 0; i < movieGenreArray.length; i++) {
                        
                                let badge = document.createElement('span');
                                badge.classList.add('badge','text-bg-success','mx-1');
                                badge.textContent = movieGenreArray[i].name;
                                genreDiv.appendChild(badge);
                            }
                        }
                        
                        // Filter the movies displayed when the particular genre button is clicked
                        async function filterByGenre(filterBtn) {
                        
                            // Get the genre ID of the btn filter that was clicked
                            let genreId = parseInt(filterBtn.getAttribute('data-genreId'));
                        
                            // Grab the movies Array from the database
                            let movies = await getMovies();
                        
                            // Declare new array to put the filtered movies into
                            let filteredMovies = [];
                        
                            // For movies that equal the genre id, put it into new movie array
                            for (let i = 0; i < movies.length; i++) {
                        
                                // Get genres for movie
                                let movieGenreIds = movies[i].genre_ids;
                        
                                // Add movie to new array if it has genre id
                                if ( movieGenreIds.includes(genreId) ) {
                                    filteredMovies.push(movies[i]);
                                }
                            }
                        
                            // Display movies based on new filtered array
                            displayMovies(filteredMovies);
                        }
                    
                

TL;DR

In order to grab data with a 3rd-party API, use the fetch method in a try...catch statement so that the app can gracefully inform the user of an issue if there is one trying to access the data. This needs to go inside an async function with the await keyword proceeding fetch since it takes awhile (comparatively) to grab the data from the API and return it.

Code Explanation

Movie Garden was created with the following functions.

getMovies grabs data about the most popular movies from "The Movide DB" (TMDB) using their API with the fetch method in a try...catch statement. With the try...catch statement the app can gracefully inform the user of an issue if there is one trying to access the data instead of just breaking. In this case it'll show a sweet alert. This needs to go inside an async function with the await keyword proceeding fetch since it takes awhile (comparatively) to grab the data from the API and return it.

getMovie is similar to getMovies except instead of using the API to get data for the most popular movies, it uses the API to get detailed data for one movie. The one movie is identified with the movieId which is a parameter for this function and used in the API call using string interpolation.

displayMovies shows the movies that are passed in as a parameter on the page. This is done by first grabbing the HTML element the cards are to be placed in. A template tag is used on the elements of the card so the same structure can be used for each of the cards. This is used in conjunction with a for loop to modify each element of the card template for each movie and add it to the element the cards are to be placed in. The most unique element that was modified is the button where a custom data-movieId attribute was set on each of the cards so that the Id of the movie could be accessed when the "More Info" button is clicked.

displayAllMovies uses getMovies to grab the popular movie data from TMBD and passes that data as an argument of displayMovies.

showMovieDetails puts the details of the movie into the modal when the "More Info" button is clicked. The button that was clicked is a parameter of this function. Using the custom data-movieId attribute on the button, movieId is passed as an argument to getMovie. The elements of the modal are then filled in the with data recieved back from the API call.

displayGenres takes in an array of genres for a specific movie as a parameter. It then gets the element from the modal that is to hold the genre badges to display all the genres associated with a particular movie.

filterByGenre takes in a clicked filter button as a parameter. From that button, the genreId is grabbed which is then used with the movies returned by getMovies. Using a for loop, each of the movies are checked to see if they have a genreId which includes the genreId of the filter button that was clicked. If it does, the movie is placed in a new array. This array is then passed as an argument to displayMovies so that only those movie cards are shown on the page.

What I learned

  • There are functions that take awhile to run. These require the await keyword to be placed before it. If an await keyword is required it needs to be inside an async function. The await keyword allows for the following function to finish doing its job before continuing with the rest of the async function. For example when used with fetch to get data from an API.
  • Use a try...catch statement to control what happens if something goes wrong when implementing resources from 3rd-party sources. For example using fetch to access data from an API about movies. If an error happens, the catch block can be coded to present the user with information about what happened and next possible actions with a great UI experience.
  • When trying to display similar elements on a page where only the content is changing, use HTML templates to easily recreate the elements and replace the content accordingly along with .content.cloneNode(true) on the template element in the DOM.

Improvements

  • Make sidebar sticky.
  • Add also "Now Playing", "Top Rated", "Upcoming", "Trending" movie lists.
  • A feature where a user can come to visit the page and their favorite movies can be saved to a list that is stored in the local storage.