Skip to content

Latest commit

 

History

History
321 lines (217 loc) · 10.8 KB

File metadata and controls

321 lines (217 loc) · 10.8 KB

Exercise 3 - First user interface

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

3.1 A simple list of movie titles

📖 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

3.1.1 Static vs. dynamic HTML

Static HTML

📖 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.

Dynamic HTML

📖 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:

  1. Frontend JavaScript code uses the fetch browser API to get JSON movie data from the backend API
  2. Frontend JavaScript code converts JSON movie data to JavaScript objects representing movies
  3. 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.

What is the DOM?

📖 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.

Accessing the DOM in our web app

📖 To create a bullet list we need to use the DOM API to:

  1. Create a <ul> element to contain the movie list
  2. Create <li> elements containing the movie titles we want to display
  3. 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 the createElement 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:

✏️ 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 id movie-list. Take a look at frontend/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 from getMoviesFromApi

  • Lastly we call the appendChild method on our movie list div element using the result from createMovieList

✏️ 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!

3.2 Create a grid of movie cards

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.

3.3 Displaying movie posters

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.