The Code
// Get all list of movies
async function getPopularMovies() {
try {
const popularMoviesUrl = 'https://api.themoviedb.org/3/movie/popular';
// the easiest way to fetch an API (asynchronous)
let response = await fetch(popularMoviesUrl, {
headers: {
Authorization: `Bearer ${API_KEY}`,
},
});
if (response.ok) {
let data = await response.json();
return data.results;
}
} catch (error) {
console.log(error)
return [];
}
}
// display all list of movies using our template
function displayMovies(movies) {
// get our movie card template
const movieCardTemplate = document.getElementById('movie-card-template');
// find the element where the movie card will go
const movieRow = document.getElementById('movie-row');
movieRow.innerHTML = ''; // when we click the button we do not need to add another list
// for each movie in the movies array
movies.forEach((movie) => {
// - copy the template
let movieCard = movieCardTemplate.content.cloneNode(true);
// -
let titleElement = movieCard.querySelector('.card-body > h5');
titleElement.textContent = movie.title;
let descriptionElement = movieCard.querySelector('.card-text');
descriptionElement.textContent = movie.overview;
let movieImgElement = movieCard.querySelector('.card-img-top');
movieImgElement.setAttribute(
'src',
'https://image.tmdb.org/t/p/w500' + movie.poster_path
);
// let's set up the id for all movies
let infoButton = movieCard.querySelector('.btn-primary');
infoButton.setAttribute('data-movieId', movie.id);
// let's set up the id for the favorite button
let favoriteButton = movieCard.querySelector('.btn-outline-primary');
favoriteButton.setAttribute('data-movieId', movie.id);
movieRow.appendChild(movieCard);
});
}
// get the movie by id (single responsibility principle)
async function getMovieById(movieId) {
try {
const movieDetailUrl = `https://api.themoviedb.org/3/movie/${movieId}?language=en-US`;
// const movieDetailUrl = 'https://api.themoviedb.org/3/movie/' + movieId
let response = await fetch(movieDetailUrl, {
headers: {
Authorization: `Bearer ${API_KEY}`,
},
});
if (response.ok) {
let data = await response.json();
return data;
}
} catch (error) {
console.log(error);
return undefined;
}
}
// get the movie details
async function getMovieDetails(infoBtn) {
// access the movie id by getting the attribute we've set on the displayMovies function
let movieId = infoBtn.getAttribute('data-movieId');
let movie = await getMovieById(movieId);
if (movie != undefined) {
// return data object
showMovieDetails(movie);
}
}
// display the single movie
function showMovieDetails(movie) {
// format number to US dollar currency
let USDollar = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});
// get the element where want to put the title, description, image
let movieTitle = document.querySelector('.movieTitle');
movieTitle.textContent = movie.title;
let movieDescription = document.querySelector('.movieDescription');
movieDescription.textContent = movie.overview;
let moviePoster = document.querySelector('.movie-poster-details');
moviePoster.setAttribute(
'src',
`https://image.tmdb.org/t/p/w500${movie.poster_path}`
);
let movieTagline = document.querySelector('.tagline');
if (movie.tagline) {
movieTagline.textContent = movie.tagline;
} else {
movieTagline.textContent = movie.title;
}
let movieBudget = document.querySelector('.budget');
if (movie.budget) {
movieBudget.textContent = USDollar.format(movie.budget);
} else {
movieBudget.textContent = 'No budget info available for now.';
}
let movieRevenue = document.querySelector('.revenue');
if (movie.revenue) {
movieRevenue.textContent = USDollar.format(movie.revenue);
} else {
movieRevenue.textContent = 'No revenue info available for now.';
}
// release-date
let releaseDate = document.querySelector('.release-date');
releaseDate.textContent = convertDate(movie.release_date);
// Accessing attributes inside the 'genres' attribute
const nameAttributes = movie.genres.map((item) => item.name);
const genresName = nameAttributes.join(', ');
let movieGenres = document.querySelector('.movie-genres');
movieGenres.textContent = genresName;
// getting the img with the class productionCompanies
let companiesLogo = document.querySelector('.productionCompanies');
companiesLogo.innerHTML = '';
// Accessing the production_companies
let productionCompanies = movie.production_companies.map(
(item) => item.logo_path
);
// getting each logo and create a new image
productionCompanies.forEach((logo) => {
// check if the logo exist
if (logo) {
// create a new image element
let newImage = document.createElement('img');
newImage.setAttribute(
'src',
`https://image.tmdb.org/t/p/w500${logo}`
);
newImage.setAttribute('class', 'w-25 pe-2');
newImage.setAttribute('alt', 'Company logo');
// add the new image to the screen
companiesLogo.appendChild(newImage);
}
});
}
// convert the date format
function convertDate(inputDate) {
const dateObject = new Date(inputDate);
// Define options for formatting
const options = {
weekday: 'short',
month: 'short',
day: 'numeric',
year: 'numeric',
};
// Use toLocaleDateString with the defined options
const formattedDate = dateObject.toLocaleDateString('en-US', options);
return formattedDate;
}
// display popular movies from the API
async function displayPopularMovies() {
let movies = await getPopularMovies();
displayMovies(movies);
}
// display the movies from the localstorge
function displayFavoriteMovies() {
// get movies from getFavoriteMovies
let movies = getFavoriteMovies();
// display them (using displayMovies)
displayMovies(movies);
}
// using local storage to save our favorites
function saveFavoriteMovies(movies) {
// serialization of the data
// turn and array of objects into string
let moviesAsString = JSON.stringify(movies);
// save a complete list of our favorite movies
localStorage.setItem('fyard-favorite-movies', moviesAsString);
}
// get our favorite movies from the local storage
function getFavoriteMovies() {
// retrive our list of favorite movies
// return the list of movies
let favoriteMovies = localStorage.getItem('fyard-favorite-movies');
// null if we don't have not saved any movies yet
if (favoriteMovies == null) {
favoriteMovies = [];
saveFavoriteMovies(favoriteMovies);
} else {
// deserialization of the data
favoriteMovies = JSON.parse(favoriteMovies);
}
return favoriteMovies;
}
// Adding Favorite movies
async function addFavoriteMovies(btn) {
// save one new movie to our list of favorites
let movieId = btn.getAttribute('data-movieId');
// get my favorite movies
let favorites = getFavoriteMovies();
let duplicateMovieId = favorites.find((movie) => movie.id == movieId);
// do not duplicate movies
if (duplicateMovieId == undefined) {
// get the whole movie
let newFavorite = await getMovieById(movieId);
if (newFavorite != undefined) {
// add the new movie to the array
favorites.push(newFavorite);
// save the new array of movies
saveFavoriteMovies(favorites);
}
}
}
// remove a favorite movie
function removeFavoriteMovies(btn) {
// remove one movie from our list of favorites
let movieId = btn.getAttribute('data-movieId');
// remove the movie from our list in local storage
let favorites = getFavoriteMovies();
favorites = favorites.filter(movie => movie.id != movieId);
saveFavoriteMovies(favorites);
// display the new list of favorites
displayFavoriteMovies()
}
API (Application Programming Interface)
Fetching APIs is a fundamental aspect of modern software development that enables applications to communicate, share data, and leverage external services, leading to more feature-rich, efficient, and interconnected software systems. APIs define the methods and data formats that applications can use to request and exchange information.
In this project we are goint to use TMDB to retrieve data from their remote servers. In our case we need to get the popular movies and more.
Let's get to it!
getPopularMovies()
The getPopularMovies
function is
responsible for communicating with the TMDB remote
server for fetching a list of popular movies. In the
server documentation we found the link that allows us to
retrieve the data.
The fetch function is used to make an asynchronous
request to the specified API endpoint
(popularMoviesUrl
). The await keyword is
used to wait for the asynchronous operation to complete,
and the result is stored in the response variable. Next,
we include an Authorization header in the API
request, and the value of the Authorization header is
set to a Bearer token, which dynamically generated using
an API_KEY
variable that i got from my TMDB
account. After that, we tcheck if the HTTP response
status is
OK (in the
range 200-299). If the response is successful,
the code proceeds to handle the data. Than we parse the
response body into a json format, and the array of
popular movies extracted from the API response. At the
end we catch any errors that might occur during the
execution of the try block. If an error occurs, the
function returns an empty array.
So our try, catch
block handles potential errors, and
returns either the array of popular movies or an empty
array in case of an error. And we design the fonction to
be used in an asynchronous context.
displayMovies()
To display the movie on the page we have created a
<template> tag that contain a card to show each
movie. This function is responsible to access the
template tag element by it's id, access the row where we
want to insert the result, and clear the row if the list
od movie already exist. The display function take an
array (a collection of movie object), all we have to do
now is to iterate through the collection, create a copy
of the template element, and modify the information
container such as the title, the overvie, the movie
poster and so one. We have also our card button, and
create a new attribute for this button for future use by
using setAttribute
. Finally we add each
movie card into our movie row using
appenChild
.
getMovieById()
The getMovieById
function allows to fetch
data from the TMDB remote server to access each movie
separately using their id.
Fisrt, we use async
to Indicates that the
function is asynchronous. In the fonction wi need a
parameter that represent the the unique identifier of
the movie. We are using a try
block that
contains the code that may potentially throw an
exception. Inside the try block we constructs the URL
for the TMDb API endpoint to fetch details of a specific
movie based on the provided movieId
. Inside
the response variable we use the fetch function to send
a GET request to the constructed API endpoint that
includes an Authorization header with an API key
(API_KEY) for authentication. Note that the
await
pauses the execution of the function
until the promise returned by fetch is resolved. Next we
are tchecking if the response status is
OK (status
code 200). If true, it means the request was
successful. We use await to pause again while parsing
the JSON data from the response body. Finally, we
use the catch
block for cases where an
error occurs within the try block. The error is logged
to the console for debugging purposes, and the function
returns undefined in case of an error.
getMovieDetails()
The getMovieDetails
is also behave
asynchronously, and take a parameter representing
the button element that triggered the function,
expecting the button to have a
data-movieId
attribute containing the
movie's unique identifier, this is why we have set the
data-movieId attribute while displaying the data. Now
all we have to do is to get this data that we have set,
so we can retrieve the movieId and store it into a
variable with the same name movieId
. Now we
can call our getMovieById
function and
passing as argument the retrieved movieId. As you know
we are using the await that pauses the execution of the
function until the promise returned by getMovieById is
resolved, and store the resolved value (the movie
details) into a movie
variable. If the
movie variable is not equal to undefined
,
the condition ensures that the movie details were
successfully retrieved, and calls the
showMovieDetails
function with the movie
details as an argument. Note that if the movie details
were not successfully retrieved (i.e.,
movie is undefined), no further action is taken.
showMovieDetails()
The showMovieDetails
function dynamically updates various HTML elements on the web page to present detailed information about a movie, including its title, description, poster, tagline, budget, revenue, release date, genres, and production companies with their logos.
The first part just create an Intl.NumberFormat
object named USDollar
to format numbers as US dollars. Then we start by updating Movie Title, Description, and Poster doing the following steps:
- Retrieve the HTML elements using
document.querySelector
for the movie's title, description, and poster. - Set the text content of the title and description elements with information from the provided movie object.
- Set the src attribute of the poster image element with the URL constructed from the movie's poster path.
- For the release date, we call the
convertDate
function defined bellow to convert the release date into a desired format. - Maps through the genres attribute of the movie object to extract genre names and joins them into a comma-separated string. Sets the text content of the genres element with this string.
- Note that for each logo path, we create a new
img
element.
convertDate()
The convertDate
function takes a date
string (inputDate) as a parameter, converts it to a
JavaScript Date object, and then formats it using the
toLocaleDateString method with specific options.
Finally, it returns the formatted date as a string. This
function helpl converting date comming from the remote
server we want to use in the movie details in a specific
format. Note that the following line of code:
const options = { weekday: 'short', month: 'short',
day: 'numeric', year: 'numeric' };
defines an options object with properties specifying the
desired format for the date. As example, the input date
'2024-02-01' is converted and formatted according
to the specified options, resulting in the output 'Thu Feb 01 2024'.
displayPopularMovies()
The displayPopularMovies
function is
designed to display popular movies with the help of
getPopularMovies
that fetching the list of
popular movies and store it in the movies variable.
Finally, the displayMovies
function is then
called with the list of popular movies as an argument.
This function is responsible for rendering the movies.
displayFavoriteMovies()
The displayFavoriteMovies
function is
designed to display the favorite movies with the help of
getFavoriteMovies
that retrieve the list of
favorite movies and store it in the movies variable.
Finally, the displayMovies
function is then
called with the list of favorite movies as an argument.
This function is responsible for rendering the movies.
As you can see, The
displayFavoriteMovies
and
displayPopularMovies
follow a similar
pattern. The key difference is the source of movies, the
displayPopularMovies likely retrieved from an
external source, and the
displayFavoriteMovies displays favorite movies
stored locally.
Talking about LocalStorage
Before going any further, let's talk a little about
localstorage. What is LocalStorage?
LocalStorage is a web
storage feature provided by web browsers to allow
websites to store data persistently on a user's device.
It provides a simple key-value storage mechanism and is
a part of the Web Storage API along with sessionStorage.
Note that both the key and the value are strings. If you
need to store non-string data types, you'll need to
convert them to strings before storing and parse them
back when retrieving. The key distinction between
localStorage
and
sessionStorage
is that data stored in
localStorage persists across browser sessions, while
data in sessionStorage is only available for the
duration of a single page session. The Data stored in
localStorage persists even after the user closes and
reopens the browser. It remains available until
explicitly cleared by the user or the website.
saveFavoriteMovies()
The saveFavoriteMovies
function purpose is
to save a list of favorite movies to the browser's local
storage. Fisrt we need to serialize the data.
Serialization is the process of converting a data
structure into a format that can be easily stored or
transmitted, in this case, converting the array of
objects into a JSON string.
The input parameter movies, that is an array of objects
representing favorite movies, is serialized into a
string using JSON.stringify()
, and stored into the
moviesAsString variable. Now the serialized string
(moviesAsString) is then saved to the local
storage using localStorage.setItem()
, and
the key used for storing this data is set to
'fyard-favorite-movies
'.
getFavoriteMovies()
The getFavoriteMovies
function purpose is to retrieving the favorite movies from the local storage.
The following line: let favoriteMovies = localStorage.getItem('fyard-favorite-movies');
attempts to retrieve the list of favorite movies from the local storage using the key 'fyard-favorite-movies'.
The result (favoriteMovies) is a string representation of the stored data. We use the if statement to check
if the retrieved data is 'null', indicating that no favorite movies have been saved yet. In this case,
an empty array (favoriteMovies = []
) is assigned to 'favoriteMovies',
and the function saveFavoriteMovies(favoriteMovies)
is called to save this empty array to local storage.
This initializes the storage with an empty list of favorite movies. In case where the retrieved data is not 'null'
(i.e., there are saved movies), the else block is executed,
and the line favoriteMovies = JSON.parse(favoriteMovies);
deserializes the string representation
of the saved data (favoriteMovies) into a JavaScript array. Note that the JSON.parse
function
is used to convert the JSON string back into its original array form.
Finally,
the function returns the list of favorite movies (favoriteMovies), which has either been retrieved
from local storage or initialized as an empty array if no movies were previously saved.
addFavoriteMovies()
The addFavoriteMovies
function Will add a new movie to our localstorage list.
Moreover, to do so, we need to pass a button (btn
) as a parameter that representing
a button that triggers the addition of a movie to the list of favorites.
Then it retrieves the movieId
from the button's data attribute that was set as:
('data-movieId'
). Next we need to get our favorite movies from the local storage
by calling our getFavoriteMovies()
function to retrieve the current list of favorite movies.
We search for the movie we want to add into the list with the help of the built-in Javascript method (find
),
and store the result in the duplicateMovieId
variable.
After that, we check if there is a movie in the favorites list with the same id
as the one being added in the duplicateMovieId
because we do not want
to have duplicate movie into our localstorage list. If there is actually no movie matching this id,
we proceed to add the movie. So, if the movie is not a duplicate, we call the getMovieById(movieId)
to get the details of the movie with the specified id.
If the movie details are successfully retrieved (newFavorite != undefined
),
we just push it to the favorites
array and save the updated list using our
saveFavoriteMovies(favorites)
function passing the new list of favorite movies.
removeFavoriteMovies()
The removeFavoriteMovies
function is responsible for removing a favorite movie from the list.
It takes a button (btn
) as a parameter, and this parameter represent the button that will get triggered
to remove a movie from the list of favorites. It retrieves the movieId from the button's data attribute,
calls the getFavoriteMovies()
function to retrieve the current list of favorite movies,
and using the filter
method to create a new array (favorites
) excluding the movie with the specified id.
After that, we call our saveFavoriteMovies()
function to saves the updated list of favorite movies.
Finally, we display the new updated list of favorite movies by calling the displayFavoriteMovies()
.