PageSourceSearch

https://superb-hamster-34c5dc.netlify.app/code

html superb-hamster-34c5dc.netlify.app collected 2026-10-03 11:28:01 UTC 24,403 bytes, 443 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="UTF-8">
6<!-- This site is hosted on Netlify. Anyone can build and deploy a site
7     like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=5c531a1f-a9d8-4863-9352-6a171d139373
8     Netlify hosting facts for this site: static/SSR served via Netlify Edge. -->
9    <meta name="viewport" content="width=device-width, initial-scale=1.0">
10    <title>Movie Garden: By Rebecca Copeland</title>
11
12    <!-- Bootstrap CSS -->
13    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
14        integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
15
16    <!-- Bootstrap Icons -->
17    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
18
19    <!-- Devicons -->
20    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/[email protected]/devicon.min.css">
21
22    <!-- Prism CSS -->
23    <link rel="stylesheet" href="/css/prism.css" />
24
25    <!-- Custom CSS -->
26    <link href="/css/site.css" rel="stylesheet" />
27    <link href="/css/themes.css" rel=stylesheet />
28
29
30    <!-- Favicon -->
31    <link rel="shortcut icon" href="/img/rebeccacopeland-favicon.png" type="image/x-icon" />
32
33</head>
34
35<!-- CHANGE THEME ACCORDINGLY TO LOGO -->
36
37<body data-rc-theme="green">
38
39    <!-- #region navbar -->
40    <nav class="navbar navbar-expand-lg navbar-dark">
41        <div class="container-fluid">
42            <a class='navbar-brand' href='/'>
43                <img src="/img/Rebecca-Copeland-logo-white.svg" height="30" />
44                Movie Garden
45            </a>
46            <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
47                data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
48                aria-label="Toggle navigation">
49                <span class="navbar-toggler-icon"></span>
50            </button>
51            <div class="collapse navbar-collapse" id="navbarSupportedContent">
52                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
53                    <li class="nav-item">
54                        <a class='nav-link' href='/'>Home</a>
55                    </li>
56                    <li class="nav-item">
57                        <a class='nav-link' href='/app'>The App</a>
58                    </li>
59                    <li class="nav-item">
60                        <a aria-current='page' class='nav-link active' href='/code'>The Code</a>
61                    </li>
62                    <li class="nav-item">
63                        <!-- CHANGE ME TO THE REAL REPO LINK -->
64                        <a class="nav-link" href="https://github.com/rebcop/MovieGarden" target="_blank">The Repo</a>
65                    </li>
66                    <li class="nav-item">
67                        <a class="nav-link" href="https://rebcop.dev/" target="_blank">Portfolio</a>
68                    </li>
69                    <li class="nav-item">
70                        <!-- CHANGE ME TO THE DEPLOYED BLOG PROJECT -->
71                        <!-- NOTE: it would be a great idea to write a blog and
72                        link here -->
73                        <a class="nav-link d-none" href="" target="_blank">Blog</a>
74                    </li>
75                </ul>
76            </div>
77        </div>
78    </nav>
79    <!-- #endregion navbar -->
80
81    <main class="container py-3">
82        <h2 class="border-bottom border-2 border-secondary-subtle">The Code</h2>
83        <div class="row">
84            <div class="col-12 col-lg-8">
85                <pre class="line-numbers">
86                    <code class="language-js">
87                        const API_KEY = "SECRET";
88
89                        // Calls the API and gets the movies, async tells it to wait, returns movie array
90                        async function getMovies() {
91                        
92                            // attempt to get all the popular movies through API
93                            try {
94                                let response = await fetch('https://api.themoviedb.org/3/movie/popular', {
95                                    headers: {
96                                        'Authorization': `Bearer ${API_KEY}`
97                                    }
98                                });
99                        
100                                // changes the string recieved back and parses it into an object
101                                let data = await response.json();
102                        
103                                return data.results;
104                        
105                            // if something goes wrong, show error alert
106                            } catch (error) {
107                                swal.fire({
108                                    icon: 'error',
109                                    backdrop: false,
110                                    title: 'Oops!',
111                                    text: 'Something went wrong reaching the TMBD API.'
112                                })
113                            }
114                        }
115                        
116                        // Calls the API and gets information for the one movie
117                        async function getMovie(movieId) {
118                        
119                            // Attempt to get movie details through API
120                            try {
121                                let response = await fetch(`https://api.themoviedb.org/3/movie/${movieId}`, {
122                                    headers: {
123                                        'Authorization': `Bearer ${API_KEY}`
124                                    }
125                                });
126                        
127                                // changes the string recieved back and parses it into an object
128                                let data = await response.json();
129                        
130                                return data;
131                        
132                            // if something goes wrong, show error alert
133                            } catch (error) {
134                                swal.fire({
135                                    icon: 'error',
136                                    backdrop: false,
137                                    title: 'Oops!',
138                                    text: 'Something went wrong reaching the TMBD API.'
139                                })
140                            }
141                        }
142
143                        // Shows all the movie cards on the page
144                        function displayMovies(movies) {
145
146                            // Get the div that the movie cards will be placed into
147                            const movieListDiv = document.getElementById('movie-list');
148
149                            // Initialize the DIV, make sure it's empty
150                            movieListDiv.textContent = '';
151
152                            // Get the template for the movie cards
153                            const moviePosterTemplate = document.getElementById('movie-card-template');
154                        
155                            // Create the movie card from the template for each movie returned in the list
156                            for (let i = 0; i &lt; movies.length; i++) {
157                        
158                                let movie = movies[i];
159                        
160                                // Copy everything in template tags
161                                let movieCard = moviePosterTemplate.content.cloneNode(true);
162                        
163                                // Uses a CSS selector to get the element and reassign the value of the img src attribute
164                                let movieImgElement = movieCard.querySelector('.card-img-top');
165                                movieImgElement.src = `https://image.tmdb.org/t/p/w500${movie.poster_path}`
166                        
167                                // Get the element holding the movie title and replace the text with the title of the movie
168                                let movieTitleElement = movieCard.querySelector('.card-body > h5');
169                                movieTitleElement.textContent = movie.title;
170                        
171                                // Get the element holding the paragraph information and replace the text with the summary of movie
172                                let movieParagraphElement = movieCard.querySelector('.card-text');
173                                movieParagraphElement.textContent = movie.overview;
174                        
175                                // Make up own attribute to assign movie id to access this information later for the modal
176                                let movieButton = movieCard.querySelector('.btn-primary');
177                                movieButton.setAttribute('data-movieId', movie.id);
178                        
179                                // Add the movie card to the page
180                                movieListDiv.appendChild(movieCard);
181                            } 
182                        
183                        }
184                        
185                        // Get the list of movies with no filter and put them on the page
186                        async function displayAllMovies() {
187                        
188                            let movies = await getMovies();
189                            displayMovies(movies);
190                            
191                        }
192                        
193                        // Put the movie details in the modal when "More Info" button is pressed
194                        async function showMovieDetails(clickedBtn) {
195                        
196                            // get the ID of the movie that was clicked
197                            let movieId = clickedBtn.getAttribute('data-movieId');
198                        
199                            // get the details of the movie with that ID from TMBD API
200                            let movieData = await getMovie(movieId);
201                        
202                            // Modify Img on Modal
203                           let movieImg = document.querySelector('#movieModal .movie-image');
204                           movieImg.src = `https://image.tmdb.org/t/p/w500${movieData.poster_path}`
205                        
206                           // Modify Title on Modal
207                           let modalTitle = document.querySelector('#movieModal .movie-title');
208                           modalTitle.textContent = movieData.title;
209                        
210                           // Modify Tagline on Modal
211                           let modalTagline = document.querySelector('#movieModal .tagline');
212                           modalTagline.textContent = `${movieData.tagline}`;
213                        
214                           // Display genres on page
215                           displayGenres(movieData.genres);
216                        
217                           // Modify overview summary of Modal
218                           let modalBody = document.querySelector('#movieModal .overview');
219                           modalBody.textContent = movieData.overview;
220                               
221                           // Modify homepage website button links to on Modal
222                           let moviePageBtn = document.querySelector('#movieModal .btn-primary');
223                           moviePageBtn.href = movieData.homepage;
224                        
225                           // Modify Release Date
226                           let releaseDateMonth = document.getElementById('month');
227                           releaseDateMonth.textContent = movieData.release_date.slice(5,7);
228                        
229                           let releaseDateDay = document.getElementById('day');
230                           releaseDateDay.textContent = movieData.release_date.slice(8,10);
231                        
232                           let releaseDateYear = document.getElementById('year');
233                           releaseDateYear.textContent = movieData.release_date.slice(0,4);
234                        
235                           // Modify Runtime
236                           let runtime = document.getElementById('runtime');
237                           runtime.textContent = movieData.runtime;
238                        
239                           // Modify Voter average
240                           let voteAvg = document.getElementById('voteAvg');
241                           voteAvg.textContent = (movieData.vote_average).toFixed(2);
242                        
243                           // Modify Voter count
244                           let voteCount = document.getElementById('voteCount');
245                           voteCount.textContent = movieData.vote_count;
246                        }
247                        
248                        // Add genre badges to genre div on page with all Genres associated with movie
249                        function displayGenres(movieGenreArray) {
250                        
251                            // Get div element to hold genre badges
252                            const genreDiv = document.getElementById('genres');
253                            genreDiv.textContent = '';
254                        
255                            // loop through each item in array and add genre element to div element
256                            for (let i = 0; i &lt; movieGenreArray.length; i++) {
257                        
258                                let badge = document.createElement('span');
259                                badge.classList.add('badge','text-bg-success','mx-1');
260                                badge.textContent = movieGenreArray[i].name;
261                                genreDiv.appendChild(badge);
262                            }
263                        }
264                        
265                        // Filter the movies displayed when the particular genre button is clicked
266                        async function filterByGenre(filterBtn) {
267                        
268                            // Get the genre ID of the btn filter that was clicked
269                            let genreId = parseInt(filterBtn.getAttribute('data-genreId'));
270                        
271                            // Grab the movies Array from the database
272                            let movies = await getMovies();
273                        
274                            // Declare new array to put the filtered movies into
275                            let filteredMovies = [];
276                        
277                            // For movies that equal the genre id, put it into new movie array
278                            for (let i = 0; i &lt; movies.length; i++) {
279                        
280                                // Get genres for movie
281                                let movieGenreIds = movies[i].genre_ids;
282                        
283                                // Add movie to new array if it has genre id
284                                if ( movieGenreIds.includes(genreId) ) {
285                                    filteredMovies.push(movies[i]);
286                                }
287                            }
288                        
289                            // Display movies based on new filtered array
290                            displayMovies(filteredMovies);
291                        }
292                    </code>
293                </pre>
294            </div>
295            <div class="col-12 col-lg-4">
296                <h4>TL;DR</h4>
297                <p>
298                    In order to grab data with a 3rd-party <span>API</span>, use the <code>fetch</code> method in a
299                    <span>try...catch statement</span>
300                    so that the app can gracefully inform the user of an issue if there is one trying to access the
301                    data. This needs to go inside an
302                    <code>async function</code> with the <code>await</code> keyword proceeding <code>fetch</code> since
303                    it takes awhile (comparatively)
304                    to grab the data from the <span>API</span> and return it.
305                </p>
306                <h4>Code Explanation</h4>
307                <p>Movie Garden was created with the following functions.</p>
308                <p>
309                    <code>getMovies</code> grabs data about the most popular movies from "The Movide DB" (TMDB) using
310                    their <span>API</span>
311                    with the <code>fetch</code> method in a <span>try...catch statement</span>. With the
312                    <span>try...catch statement</span>
313                    the app can gracefully inform the user of an issue if there is one trying to access the data
314                    instead of just breaking. In this case it'll show a <span>sweet alert</span>. This needs to go
315                    inside an
316                    <code>async function</code> with the <code>await</code> keyword proceeding <code>fetch</code>
317                    since it takes awhile (comparatively) to grab the data from the API and return it.
318
319                </p>
320                <p>
321                    <code>getMovie</code> is similar to <code>getMovies</code> except instead of using the
322                    <span>API</span> to get data for the most popular movies,
323                    it uses the <span>API</span> to get detailed data for one movie. The one movie is identified with
324                    the <code>movieId</code> which is a <span>parameter</span>
325                    for this <span>function</span> and used in the <span>API</span> call using <span>string
326                        interpolation</span>.
327                </p>
328                <p>
329                    <code>displayMovies</code> shows the movies that are passed in as a <span>parameter</span> on the
330                    page. This is done by first grabbing the <span>HTML element</span> the cards are to be placed in. A
331                    <span>template tag</span>
332                    is used on the elements of the card so the same structure can be used for each of the cards. This is
333                    used in conjunction with a <span>for loop</span> to modify each <span>element</span> of the
334                    card template for each movie and add it to the element the cards are to be placed in. The most
335                    unique element that was modified is the button where a <span>custom <code>data-movieId</code>
336                        attribute</span>
337                    was set on each of the cards so that the Id of the movie could be accessed when the "More Info"
338                    button is clicked.
339                </p>
340                <p>
341                    <code>displayAllMovies</code> uses <code>getMovies</code> to grab the popular movie data from TMBD
342                    and passes that data as an argument of <code>displayMovies</code>.
343                </p>
344                <p>
345                    <code>showMovieDetails</code> puts the details of the movie into the <span>modal</span> when the
346                    "More Info" button is clicked. The button that was clicked is a parameter of this function.
347                    Using the <span>custom <code>data-movieId</code> attribute</span> on the button, movieId is passed
348                    as an <span>argument</span> to <code>getMovie</code>.
349                    The elements of the modal are then filled in the with data recieved back from the <span>API</span>
350                    call.
351
352                </p>
353                <p>
354                    <code>displayGenres</code> takes in an <span>array</span> of genres for a specific movie as a
355                    <span>parameter</span>. It then gets the element from the modal that is to hold the genre badges
356                    to display all the genres associated with a particular movie.
357                </p>
358                <p>
359                    <code>filterByGenre</code> takes in a clicked filter button as a parameter. From that button, the
360                    genreId is grabbed which is then used with the movies returned by <code>getMovies</code>.
361                    Using a <span>for loop</span>, each of the movies are checked to see if they have a genreId which
362                    includes the genreId of the filter button that was clicked. If it does, the movie is placed
363                    in a new array. This array is then passed as an argument to <code>displayMovies</code> so that only
364                    those movie cards are shown on the page.
365                </p>
366
367                <h4>What I learned</h4>
368                <ul>
369                    <li>There are functions that take awhile to run. These require the <code>await</code> keyword to be
370                        placed before
371                        it. If an <code>await</code> keyword is required it needs to be inside an
372                        <code>async function</code>. The <code>await</code> keyword
373                        allows for the following function to finish doing its job before continuing with the rest of the
374                        <code>async function</code>. For example when used with <code>fetch</code> to get data from an
375                        <span>API</span>.
376                    </li>
377                    <li>Use a <span>try...catch statement</span> to control what happens if something goes wrong when
378                        implementing
379                        resources from 3rd-party sources. For example using <code>fetch</code> to access data from an
380                        <span>API</span> about movies.
381                        If an error happens, the <span>catch block</span> can be coded to present the user with
382                        information about
383                        what happened and next possible actions with a great <span>UI</span> experience.
384                    </li>
385                    <li>
386                        When trying to display similar elements on a page where only the content is changing, use
387                        <span>HTML
388                            templates</span> to easily recreate the elements and replace the content accordingly along
389                        with
390                        <code>.content.cloneNode(true)</code> on the template element in the DOM.
391                    </li>
392                </ul>
393
394                <h4>Improvements</h4>
395                <ul>
396                    <li>Make sidebar sticky.</li>
397                    <li>Add also "Now Playing", "Top Rated", "Upcoming", "Trending" movie lists.</li>
398                    <li>A feature where a user can come to visit the page and their favorite movies can be saved to a
399                        list that is stored in the <span>local storage</span>.</li>
400                </ul>
401            </div>
402        </div>
403    </main>
404
405    <footer class="py-3">
406
407        <div class="container">
408            <div class="row align-items-center row-cols-1 row-cols-lg-3 gy-2">
409                <div class="col text-center text-lg-start">
410                    &copy; 2023 Rebecca Copeland
411                </div>
412                <div class="col text-center">
413                    <a class="nav-link" href="https://rebcop.dev/" target="_blank"><img
414                            src="/img/Rebecca-Copeland-full-logo-white.svg" height="40" /></a>
415                </div>
416                <div class="col text-center text-lg-end">
417                    <a href="https://www.linkedin.com/in/rebcop/" target="_blank"><i class="bi bi-linkedin"></i></a>
418                    <a href="https://github.com/rebcop" target="_blank"><i class="bi bi-github"></i></a>
419                </div>
420            </div>
421        </div>
422
423    </footer>
424
425    <!-- Bootstrap JS -->
426    
426<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
427        integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
428        crossorigin="anonymous"></script>
428
429
430    <!-- Prism JS -->
431    
431<script src="/js/prism.js"></script>
431
432    
432<script>
433        Prism.plugins.NormalizeWhitespace.setDefaults({
434            'remove-trailing': true,
435            'remove-indent': true,
436            'left-trim': true,
437            'right-trim': true
438        })
439    </script>
439
440
441</body>
442
443</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.