This exercise is focused on creating a nice user interface in order to display the movie data to the user. Since we prepared the serverside part in the last exercise, this exercise will focus on frontend code.
You will learn to:
- Create a list that displays the movie titles
- Create a grid of cards to display movie data
- Display movie posters in the cards
📖 In order to display a list of movie titles, we need some HTML. Bullet lists (unordered lists) in HTML looks like this:
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
📖 Each <li>
tag inside a <ul>
represents a list item.
Visually it should look something like this in the browser:
- List item 1
- List item 2
- List item 3
📖 Now we know what HTML we want to use to display the movie titles. But how do we actually insert the markup into the web page?
✏️ Open index.html
and try creating a list like the one above. Place the markup inside the <body>
tag.
✏️ Verify that the bullet list you created works by opening up the web app in Chrome.
📖 We could write a list of movie titles by hand and insert it into index.html
like we just did, but since index.html
is a static file the list would not stay updated if the data changes.
✏️ Remove the list you just inserted to get ready for the next part.
📖 We want the movie list to change dynamically when the backend movie data changes. In order to do this, we need to render the data into HTML dynamically in the frontend.
📖 Here is an overview of the steps required:
- Frontend JavaScript code uses the
fetch
browser API to get JSON movie data from the backend API - Frontend JavaScript code converts JSON movie data to JavaScript objects representing movies
- Frontend JavaScript code dynamically creates and inserts HTML with movie titles into the page
📖 We solved the first step and second step in the last exercise in our getMoviesFromApi
function inside api.js
.
📖 Now we need to create some code that renders the data into HTML using the DOM API.
📖 The Document Object Model (DOM) is the data representation of the structure and content of a document on the web.
📖 The DOM represents an HTML document in browser memory, and we can use the DOM API in the browser to manipulate the document.
📖 Consider the following HTML document:
<html>
<body>
<h1>Hello Nerdschool!</h1>
</body>
</html>
📖 In the DOM, this document would be represented as a tree structure of nodes, where <html>
element would be the root node and the <body>
element would be a child node of <html>
.
📖 For example, to locate the <h1>
element in the DOM, we can use the queryselectorAll
DOM method on the document
object:
const h1 = document.querySelector('h1'); // Returns an DOM Element
console.log(h1.innerText); // innerText is a Element property containing the text of the node: 'Hello Nerdschool!'
📖 Common DOM API uses:
- Create new element
- Append element as child of existing element in document
- Remove elements
- Change existing element's contents
- Traverse elements
💡 Se Introduction to the DOM on MDN for more info.
📖 To create a bullet list we need to use the DOM API to:
- Create a
<ul>
element to contain the movie list - Create
<li>
elements containing the movie titles we want to display - Append the
<li>
as children of the<ul>
element
✏️ Create a new file called dom.js
inside the src/frontend/
foldert and insert the following function:
export const createMovieList = (movies) => {
const moviesList = document.createElement('ul');
for (const movie of movies) {
const movieListEntry = document.createElement('li');
const { title } = movie;
movieListEntry.innerText = title;
moviesList.appendChild(movieListEntry);
}
return moviesList;
}
📖 Let's take a look at what this function does:
- It takes an array of
movies
as a parameter and returns an DOM Element (renders data into HTML) - It creates a
<ul>
DOM Element using thecreateElement
DOM API method - It iterates over the
movies
array and creates a<li>
Element for each movie - The movie title for each movie is set as the
innerText
property value for each<li>
Element - Each
<li>
Element is appended as a child Element of the<ul>
Element
Relevant API documentation:
- document.createElement on MDN
- Node.appendChild on MDN
- HTMLElement.innerText on MDN
✏️ Open main.js
from the /src/frontend
folder and add the following code to the top to import createMovieList
from dom.js
:
import { createMovieList } from './dom.js';
✏️ Add the following code at the bottom of main.js
:
const movieListContainer = document.getElementById('movie-list');
const moviesList = createMovieList(movies);
movieListContainer.appendChild(moviesList);
📖 Stuck? Click "Show solution" to see what main.js
should look like:
Show solution
import { createMovieList } from './dom.js';
import { getMoviesFromApi } from "./api.js";
const movieData = await getMoviesFromApi();
const { movies } = movieData;
console.log(movies);
const movieListContainer = document.getElementById('movie-list');
const moviesList = createMovieList(movies);
movieListContainer.appendChild(moviesList);
Let's break this code down:
-
First we use
document.getElementById
to get a reference to an existing element with the idmovie-list
. Take a look atfrontend/index.html
, and you will notice an empty div:<div id="movie-list"></div>
-
We are going to append our movie list as a child of this div element.
-
Next we call
createMovieList
using the result fromgetMoviesFromApi
-
Lastly we call the
appendChild
method on our movie list div element using the result fromcreateMovieList
✏️ Open up the web app in Chrome. You should now see a list of movies:
- The Godfather
- The Shawshank Redemption
- The Godfather Part II
- ...
✏️ Now that we have the basic pattern established, time to make the GUI a bit more interesting!
In order to display our movie data we want to create a grid of movie cards, where each card displays the movie title and overview.
📖 To make this a bit easier we have prepared some CSS and a markup structure:
<div id="{movie-id}" class="movie-card">
<div class="content">
<h2>Movie title</h2>
<p>Movie overview</p>
</div>
</div>
✏️ First we need some code to create movie card elements. Add the following code to dom.js
:
const createMovieCard = (movie) => {
const movieCard = document.createElement('div');
movieCard.id = movie.id;
movieCard.className = 'movie-card';
// Create a div container containing a header and a
// paragraph for the title and overview of a movie.
const contentContainer = document.createElement('div');
contentContainer.className = 'content';
const movieHeader = document.createElement('h2');
movieHeader.innerText = movie.title;
const movieOverview = document.createElement('p');
movieOverview.innerText = movie.overview;
contentContainer.appendChild(movieHeader);
contentContainer.appendChild(movieOverview);
movieCard.appendChild(contentContainer);
return movieCard;
}
export const createMovieCards = (movies) => {
const movieCards = [];
for (const movie of movies) {
const movieCard = createMovieCard(movie);
movieCards.push(movieCard);
}
return movieCards;
}
📖 createMovieCard
is a function that takes a movie object as parameter and returns an HTML element with the structure previously described.
📖 createMovieCards
is a function that takes an array of movie objects as parameter and returns an array of HTML elements.
✏️ Open main.js
and add createMovieCards
to the dom.js
import statement at the top of the file:
import { createMovieList, createMovieCards } from './dom.js';
✏️ At the bottom of the file, add the following code:
const movieCardsContainer = document.getElementById('movie-cards');
const movieCards = createMovieCards(movies);
for(const movieCard of movieCards) {
movieCardsContainer.appendChild(movieCard);
}
📖 This code calls createMovieCards
with a list of movies from the API to create an array of HTML elements.
📖 It then iterates over the array and appends each element to a existing div element defined in index.html
with the id movie-cards
.
✏️ Open the web app in Chrome and verify that everything is working.
✏️ Notice that we still are rendering the bullet list. We don´t need that list anymore, so go ahead and remove the bullet list-related code from main.js
to remove it from the UI.
In order to make the UI even more fun, we are going to add movie posters to each movie card.
✏️ Open dom.js
and add the following code marked in green (without the +
) to createMovieCard
:
...
contentContainer.appendChild(movieHeader);
contentContainer.appendChild(movieOverview);
// Create a div container and a image element
// to position and show the image.
+ const movieImage = document.createElement('img');
+ movieImage.src = movie.posterUrl;
+ const imageContainer = document.createElement('div');
+ imageContainer.className = 'image-container';
+ imageContainer.appendChild(movieImage);
// Add the containers containing the image
// and text content to the card
+ movieCard.appendChild(imageContainer);
movieCard.appendChild(contentContainer);
return movieCard;
📖 This change will result in the HTML code for a movie card looking like this:
<div id="{movie-id}" class="movie-card">
<div class="image-container">
<img src="{movie-poster-url}" />
</div>
<div class="content">
<h2>Movie title</h2>
<p>Movie overview</p>
</div>
</div>
📖 Notice the new image-container
div element with an img
element in it.
✏️ Open the web app in Chrome. You should now see movie posters for each movie displayed in each card.