From a9773a0c218d504085761d5a02d97f1ad19e103e Mon Sep 17 00:00:00 2001 From: Kristie Yorkston <kyorkston@gmail.com> Date: Fri, 8 Nov 2019 16:02:42 +0000 Subject: [PATCH 1/6] create a trivia quiz API fetching tutorial --- js/lesson4/tutorial.md | 135 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index ea9b8549..05686500 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -227,6 +227,141 @@ Well done, you've finished! For a bonus, switch your `getGithubInfo` method to r > Coach... explain the difference between synchronous and asynchronous requests. There's a good explanation on [Mozilla Developer Network (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests) +## ~~Exercise 2 - Trivia!~~ + + +For this exercise, we will build a web app that generates trivia questions, using this open trivia questions api: https://opentdb.com/api_config.php + +### What we will be doing: + +1. Retrieve and log quiz questions `https://opentdb.com/api.php?amount=10` + +2. Write a function that displays each question + +3. **Bonus** Set the quiz difficulty, category or type using the API URL generator. Then display whatever you'd like with the API response data. + +4. **Bonus Bonus** Add a button to reveal the answers to the questions + +### Retrieve and render log questions + +Firstly, here is an introduction to fetch() from Google - [Introduction to fetch() by Matt Gaunt](https://developers.google.com/web/updates/2015/03/introduction-to-fetch) + +There is a whole lot of jargon in there but what the fetch() function does is simplify things for the developer when making a request to an api. + +Lets first create the fetch request to the trivia api: + +```js +fetch('https://opentdb.com/api.php?amount=10') + .then(function(response) { + return response.json() + }) + .then(function(data) { + //work with the json response data here + console.log(data) + }) + }) + +``` +We send a request with `fetch`, returning a Promise to us in the form of the response. This response is passed into a +`then()` function (a Promise) where we transform it into something readable - JSON. When that Promise is resolved _then_ (`then()`) we can use this `response.json()` how we want to use it in our code when that Promise is resolved (a `console.log()` in this example). +For more information on Promises and asynchronous functions check out [javascript.info/promise-basics](https://javascript.info/promise-basics). But we're here to play with APIs so let's get on with the tutorial. + +Now open the `index.html` file in a web browser and then inspect the page - here is an article on how to do so in every browser: [How to Inspect Web Page Elements](https://www.lifewire.com/get-inspect-element-tool-for-browser-756549). +Once you have done so a panel will appear and it will show you the html elements on your page. In this panel there is a 'Console' tab, click this and see what has been logged. + +You should see something that looks like this: +```js +{ + "response_code": 0, + "results": [ + { + "category": "Entertainment: Video Games", + "type": "multiple", + "difficulty": "easy", + "question": "How many games in the Crash Bandicoot series were released on the original Playstation?", + "correct_answer": "5", + "incorrect_answers": [ + "4", + "3", + "6" + ] + }, + { + "category": "Entertainment: Video Games", + "type": "multiple", + "difficulty": "easy", + "question": "League of Legends, DOTA 2, Smite and Heroes of the Storm are all part of which game genre?", + "correct_answer": "Multiplayer Online Battle Arena (MOBA)", + "incorrect_answers": [ + "Real Time Strategy (RTS)", + "First Person Shooter (FPS)", + "Role Playing Game (RPG)" + ] + }, + ... + ] +} +``` + +It is returning 10 questions because we asked the api to return 10 with the query parameter `?amount=10` on the end of the URL. + +Okay lets now put the fetch request inside a function: +```js +function fetchQuizQuestions() { + // add fetch function in here +} +``` + +### Write a function that displays each question + +Now we need to render the questions onto our webpage. We will add them in a list format, for that we use the <li> html tag. This will be going inside the <ul> tag that we have in `index.html`, it has the id "questions". Refer back to Lesson 2 on how to create html elements and add data to them. + +```js +function renderQuizQuestions() { + +} +``` + +Put this into the second `then()` (Promise) of your fetch function, something like: + +```js +fetch(...) +.then(...) +.then(function(data) { + renderQuizQuestions(data) +}) +``` + +Because fetch() is an asynchronous function the rest of the code in your file could run at the same time as the fetch +function, meaning that the fetched data may not appear if used in an outside function, for example: + +```js +function fetchQuizQuestions() {...} + +function renderQuizQuestions() { + const data = fetchQuizQuestions() + + // render the data into UI components +} +``` + +The `data` inside `renderQuizQuestions()` will come back as undefined as it is run before the asynchronous function has resolved. + +So now you should hopefully have some Questions rendered onto your webpage, if not please ask for help from a Codebar coach, a fellow Codebar attendee or checkout the Codebar Slack chatrooms. + +### **BONUS** Play around with the API URL generator + +The trivia api we're using is pretty cool and you can configure it in a few ways, check it out: [https://opentdb.com/api_config.php](https://opentdb.com/api_config.php). +If you select a few of the options and then generate an api, you could get something like this: + +`https://opentdb.com/api.php?amount=20&category=10&difficulty=medium` + +We have `amount=20`, this is the amount of questions. `category=10` this is the book category, so each category is mapped to a number in the API. And we have `difficulty=medium`, setting the difficulty of the questions. +You can play around with this a lot but here let's set the difficulty to hard and generate an API URL with some general knowledge questions. + + +There's a lot of data that comes with the response as well, we could set a new + ## ~~Exercise 2 - BBC's tomorrow's TV schedule~~ From 61422c90bb56db885f05492d2ff5af9d01df19d1 Mon Sep 17 00:00:00 2001 From: Kristie Yorkston <kyorkston@gmail.com> Date: Fri, 8 Nov 2019 16:13:41 +0000 Subject: [PATCH 2/6] remove BBC API tutorial --- js/lesson4/tutorial.md | 217 +---------------------------------------- 1 file changed, 4 insertions(+), 213 deletions(-) diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index 05686500..b33c5ec8 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -326,10 +326,10 @@ Put this into the second `then()` (Promise) of your fetch function, something li ```js fetch(...) -.then(...) -.then(function(data) { - renderQuizQuestions(data) -}) + .then(...) + .then(function(data) { + renderQuizQuestions(data) + }) ``` Because fetch() is an asynchronous function the rest of the code in your file could run at the same time as the fetch @@ -359,217 +359,8 @@ If you select a few of the options and then generate an api, you could get somet We have `amount=20`, this is the amount of questions. `category=10` this is the book category, so each category is mapped to a number in the API. And we have `difficulty=medium`, setting the difficulty of the questions. You can play around with this a lot but here let's set the difficulty to hard and generate an API URL with some general knowledge questions. - There's a lot of data that comes with the response as well, we could set a new -## ~~Exercise 2 - BBC's tomorrow's TV schedule~~ - - - -**Part 2 of this exercise is no longer possible as the API that it uses has been taken down by the BBC.** -**Sorry, we're working on fixing the exercise!** - -[Download](https://gist.github.com/despo/05cab2f0b38bc02318e7/download) the exercise files or clone them directly from github `git clone https://gist.github.com/05cab2f0b38bc02318e7.git` - -For the second exercise, we will build an application that retrieves tomorrow's TV schedule for each genre using BBC's API. - -### What we will be doing: - -1. Retrieve and render available genres using `http://www.bbc.co.uk/tv/programmes/genres.json` - -2. Write a function that retrieves tomorrow's TV schedule using a genre `http://www.bbc.co.uk/tv/programmes/genres/<genre>/schedules/tomorrow.json` - -3. Write a function that displays each programme - -4. **Bonus** Retrieve all upcoming episodes of a programme - -### Request using jQuery - -This time, let's use jQuery's `ajax()` method. Things are a bit easier when using jQuery as we can create different code blocks that handle successful or failed requests. - -Also, jQuery isolates us from the differences between browser implementations of AJAX calls (for example, if we wanted to make the previous AJAX call work in Internet Explorer, we will have to write [a much longer method](http://www.tutorialspoint.com/ajax/ajax_browser_support.htm)!) - -```js -$.ajax({ - url: request_url, - dataType: 'json', - beforeSend: function() { - // do something before running the request - } -}).done(function(data) { - // process data -}).fail(function() { - // code -}).always(function() { - // code that runs regardless of request succeeding or failing -}); -``` - -`datatype` defines the type of result we will be getting back. This avoids us having to parse the response to JSON. - -`beforeSend` can be used if we need to perform something before running the request. - -`.done()` handles a response that returns a success status code - -`.fail()` is called when the request fails - - -## Retrieving and displaying all available genres - -Write a function `retrieveGenres()` that does an AJAX call to the API. - -```javascript -function retrieveGenres() { - // AJAX call using jQuery that retrieve and process the result -} -``` -> Try logging the resulted data and have a look in the console to see and explore the created objects - -> `<key>` is the genre format we need to retrieve results from the API - -> You can use `<title>` to display a humanly readable format of the Genre - -As you can see from the console, the resulting objects are returned inside an Array. We want to iterate over the list using the native Array `forEach( )` function and add each item to the `#genres` list, as a **list item**. As we need to have access to the `key` as well, we can set that as the list item's `id`. - -Now that we have all the available genres, we can move on to making calls to the API using the genre to retrieve tomorrow's schedule!! - -## Retrieve schedule - -Now, let's create a function that retrieves films using genre. - -```javascript -function getTomorrowsSchedule(genre) { - // call to retrieve TV schedule -} -``` - -The response you get back should look similar to this, with multiple objects in the broadcasts array. - -```json -{ - "broadcasts": [ - { - "is_repeat": false, - "is_blanked": false, - "schedule_date": "2014-01-15", - "start": "2014-01-15T00:10:00Z", - "end": "2014-01-15T01:50:00Z", - "duration": 6000, - "service": { - "type": "tv", - "id": "bbc_one", - "key": "bbcone", - "title": "BBC One", - "outlets": [ - { - "id": "bbc_one_wales", - "key": "wales", - "title": "Wales" - }, - { - "id": "bbc_one_wales_hd", - "key": "wales_hd", - "title": "Wales HD" - } - ] - }, - "programme": { - "type": "episode", - "pid": "b00sbk03", - "position": null, - "title": "Disturbia", - "short_synopsis": "Thriller about a high school student convinced that his neighbour is a serial killer.", - "media_type": "audio_video", - "duration": 6000, - "image": { - "pid": "p01gqbj3" - }, - "display_titles": { - "title": "Disturbia", - "subtitle": "" - }, - "first_broadcast_date": "2010-05-03T22:30:00+01:00", - "ownership": { - "service": { - "type": "tv", - "id": "bbc_three", - "key": "bbcthree", - "title": "BBC Three" - } - }, - "is_available_mediaset_pc_sd": false, - "is_legacy_media": false - } - }] - } -``` - -To process the response, we want to iterate over the `response.broadcasts` array and add each item, to `#programmes` as a list item. - -To make this a bit easier, this is how you can access the values we need: - -* `item.programme.display_titles.title` -* `item.programme.short_synopsis` -* `item.programme.image.pid` _only if `item.programme.image` is set_ -* `item.start` and `item.end` -* `item.duration` -* `item.service.title` - -It would be easier to use string concatenation to construct the html, before appending each item to the list. -Also, to make your code easier to read, try constructing the html in a method that you pass the response object. - -```javascript -function processEpisode(episode) { - var item_html = '<li>'; - item_html += '<h2>' + episode.programme.display_titles.title + '</h2>'; - // display short short synopsis - // display image - // display date and time - // display duration (HINT: the duration is in seconds, convert that to minutes) - // display the channel (or service, as its called by the API) - add this in a span with the class `service` - ... -} -``` - -> To display the date formatted correctly, you can use the `formatDate( )` function as we won't be going into details about dates in this tutorial. If you want to know how it works, try going through the code and ask your coach any questions you have. - -To display an image for a programme, we need to use `<img src=http://ichef.bbci.co.uk/images/ic/272x153/<pid>.jpg />`. As not all programmes have an image, we can use an image placeholder when no image is set `<img src='http://placehold.it/272x153' />` - -### Binding the call to the click event - -Handle a `click` event on `#genres li` and make a call to `getTomorrowsSchedule(genre)` - -### Improving our function - -To make the genre we have just clicked active, we also want to add the CSS class `active` to the element that the event has been triggered from. Don't forget to remove the class `active` from any other `#genres li` items. - -> Did you remember to commit your changes? - -### Using `beforeSend` - -Every time we issue a call to the API, we want to clear the `#programmes` list. We can do that using `empty()`. -Also, as some of the requests take a while, we want to display a spinning image `<div class='spinner'><img src='spinner.gif' /></div>`. - -> Don't forget to remove the spinner, when the request is completed successfully. - -## Bonus: Retrieving all upcoming episodes of a programme - -To get back all the upcoming shows for an episode, we need to utilise the programme pid, that we can retrieve from the response using `episode.programme.programme.pid`. The URL for the request is `http://www.bbc.co.uk/programmes/<pid>/episodes/upcoming.json` - -> **Hint:** The programme `pid` is only available if `episode.programme.position` is set. - - -```javascript -function getUpcomingEpisodes(pid) { - // AJAX call to retrieve upcoming episodes -} -``` -Since the response structure is similar to the one for retrieving tomorrow's schedule, we should be able to re-use the `processEpisode( )` function to display each item from the broadcasts array. - -Handle the `click` event to retrieve and display the upcoming episodes! - -Here is our version of the [tv schedule app](../../examples/tv-schedule/index.html). - --- This ends our **HTTP Requests, AJAX and APIs** tutorial. Is there something you don't understand? Try and go through the provided resources with your coach. If you have any feedback, or can think of ways to improve this tutorial [send us an email](mailto:feedback@codebar.io) and let us know. From 467cd2431a21681b2caeef0987e30e6678eaf5e1 Mon Sep 17 00:00:00 2001 From: kyorkston <kyorkston@gmail.com> Date: Fri, 10 Jan 2020 16:14:18 +0000 Subject: [PATCH 3/6] add link to gist --- js/lesson4/tutorial.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index b33c5ec8..5331d3db 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -231,6 +231,7 @@ Well done, you've finished! For a bonus, switch your `getGithubInfo` method to r For this exercise, we will build a web app that generates trivia questions, using this open trivia questions api: https://opentdb.com/api_config.php +[Download](https://gist.github.com/kyorkston/a2768287fe15fc693da3dc62e8a8d697/download) the exercise files or clone them directly from github `git clone https://gist.github.com/a2768287fe15fc693da3dc62e8a8d697.git`. Shoutout to [despo](https://gist.github.com/despo) for originially making these files! ### What we will be doing: @@ -240,8 +241,6 @@ For this exercise, we will build a web app that generates trivia questions, usin 3. **Bonus** Set the quiz difficulty, category or type using the API URL generator. Then display whatever you'd like with the API response data. -4. **Bonus Bonus** Add a button to reveal the answers to the questions - ### Retrieve and render log questions Firstly, here is an introduction to fetch() from Google - [Introduction to fetch() by Matt Gaunt](https://developers.google.com/web/updates/2015/03/introduction-to-fetch) @@ -357,9 +356,11 @@ If you select a few of the options and then generate an api, you could get somet `https://opentdb.com/api.php?amount=20&category=10&difficulty=medium` We have `amount=20`, this is the amount of questions. `category=10` this is the book category, so each category is mapped to a number in the API. And we have `difficulty=medium`, setting the difficulty of the questions. -You can play around with this a lot but here let's set the difficulty to hard and generate an API URL with some general knowledge questions. +You can play around with this a lot and its a simple interface and you can make the questions as easy or as difficult as you want! -There's a lot of data that comes with the response as well, we could set a new +Now that you can render the questions how about now rendering the answers? +Perhaps you could have a button to reveal the answer? +Or if you select the multi-choice questions you could add some fun css to let the user know if they got the answer right or wrong! --- From 0f93362936ae2c63d32bddd2dd4e6133156ba4a6 Mon Sep 17 00:00:00 2001 From: kyorkston <kyorkston@gmail.com> Date: Fri, 31 Jan 2020 14:51:04 +0000 Subject: [PATCH 4/6] add example app --- examples/trivia-quiz/index.html | 24 ++++ examples/trivia-quiz/index.js | 75 ++++++++++++ .../{tv-schedule => trivia-quiz}/spinner.gif | Bin examples/trivia-quiz/style.css | 40 ++++++ examples/tv-schedule/index.html | 23 ---- examples/tv-schedule/script.js | 114 ------------------ examples/tv-schedule/style.css | 110 ----------------- js/lesson4/tutorial.md | 63 +++++++--- 8 files changed, 183 insertions(+), 266 deletions(-) create mode 100644 examples/trivia-quiz/index.html create mode 100644 examples/trivia-quiz/index.js rename examples/{tv-schedule => trivia-quiz}/spinner.gif (100%) create mode 100644 examples/trivia-quiz/style.css delete mode 100644 examples/tv-schedule/index.html delete mode 100644 examples/tv-schedule/script.js delete mode 100644 examples/tv-schedule/style.css diff --git a/examples/trivia-quiz/index.html b/examples/trivia-quiz/index.html new file mode 100644 index 00000000..149b5150 --- /dev/null +++ b/examples/trivia-quiz/index.html @@ -0,0 +1,24 @@ +<html> + <head> + <meta charset="utf-8"/> + <link rel="stylesheet" href="styles.css"> + <script src="index.js"></script> + <title>codebar.io - Trivia!</title> + </head> + <body> + <header> + <img id="logo" src='https://raw.github.com/codebar/planner/master/app/assets/images/logo.png' width='200' /> + </header> + <div id="container"> + <div id="selector-container"> + <h2>Select Difficulty:</h2> + </div> + <h1>Questions</h1> + <div id="questions"> + </div> + </div> + <footer> + <div id="content"> by <a href="http://codebar.io">codebar</a> </div> + </footer> + </body> +</html> \ No newline at end of file diff --git a/examples/trivia-quiz/index.js b/examples/trivia-quiz/index.js new file mode 100644 index 00000000..7644485d --- /dev/null +++ b/examples/trivia-quiz/index.js @@ -0,0 +1,75 @@ +// As fetch() is an asynchronous function we can add the async/await keywords +async function fetchQuizQuestions(difficulty) { + return await fetch(`https://opentdb.com/api.php?amount=10&difficulty=${difficulty}`) + .then((response) => { + return response.json() + }) + .then((data) => { + renderQuizQuestions(data) + }) +} + +function renderQuizQuestions(data) { + const container = document.getElementById('questions') + //when new data is fetched we want to remove the pre-existing list + container.removeChild(container.firstChild) + + const list = document.createElement('ul') + container.appendChild(list) + + data.results.map((questionObject) => { + const listElement = document.createElement('li') + const text = document.createTextNode(questionObject.question) + listElement.appendChild(text) + list.appendChild(listElement) + }) +} + +// Bonus part of the tutorial functions +function createSelector() { + const difficulties = ['easy', 'medium', 'hard'] + + const selectorContainer = document.getElementById('selector-container') + const selector = document.createElement('select') + selector.id = 'selector' + + difficulties.map((difficulty) => { + const option = new Option(difficulty, difficulty) + selector.appendChild(option) + }) + selectorContainer.appendChild(selector) +} + +function selectorChangeListener() { + const selector = document.getElementById('selector') + + selector.addEventListener('change', event => { + const difficulty = event.target.value.toLowerCase() + + fetchQuizQuestions(difficulty) + }) +} +// // // + +function renderApp() { + createSelector() + // set to easy to load the page with + fetchQuizQuestions('easy') + selectorChangeListener() +} + +document.addEventListener('readystatechange', event => { + const readyState = event.target.readyState + const container = document.getElementById('questions') + + if (readyState === 'interactive') { + const image = new Image(100, 100) + image.src = 'spinner.gif' + + container.appendChild(image) + } else if (readyState === 'complete') { + //remove spinner when document is ready + container.removeChild(container.firstChild) + renderApp() + } +}) \ No newline at end of file diff --git a/examples/tv-schedule/spinner.gif b/examples/trivia-quiz/spinner.gif similarity index 100% rename from examples/tv-schedule/spinner.gif rename to examples/trivia-quiz/spinner.gif diff --git a/examples/trivia-quiz/style.css b/examples/trivia-quiz/style.css new file mode 100644 index 00000000..2314ce0a --- /dev/null +++ b/examples/trivia-quiz/style.css @@ -0,0 +1,40 @@ +body { + font-family: Helvetica, Arial, sans-serif; + border-top: 5px solid #4c4066; + margin: 0px auto; + font-size: 16px; +} + +header { + text-align: center; +} + +li { + padding-bottom: 10px; +} + +#selector { + font-size: 16px; +} + +#questions { + max-width: 750px; +} + +#logo { + position: relative; + top: 2px; +} + +#container { + width: 1040px; + padding-left: 8px; + min-height: 750px; +} + +footer { + padding-bottom: 10px; + border-bottom: 5px solid #4c4066; + position: absolute; + width: 100%; +} diff --git a/examples/tv-schedule/index.html b/examples/tv-schedule/index.html deleted file mode 100644 index 4622124c..00000000 --- a/examples/tv-schedule/index.html +++ /dev/null @@ -1,23 +0,0 @@ -<html> - <head> - <link rel="stylesheet" href="style.css"> - <title>codebar.io - What's on TV tomorrow?</title> - <script type="text/javascript" src="../colorpicker/jquery.js"></script> - <script type="text/javascript" src="script.js"></script> - </head> - <body> - <header> - <img id="logo" src='https://raw.githubusercontent.com/codebar/planner/master/app/assets/images/logo.png' width='200' /> - </header> - <div id="container"> - <h1>What's on TV tomorrow?</h1> - <ul id="genres"> - </ul> - <ul id="programmes"> - </ul> - </div> - <footer> - <div id="content"> by <a href="https://codebar.io/">codebar</a> </div> - </footer> - </body> -</html> diff --git a/examples/tv-schedule/script.js b/examples/tv-schedule/script.js deleted file mode 100644 index 792f07bc..00000000 --- a/examples/tv-schedule/script.js +++ /dev/null @@ -1,114 +0,0 @@ -function formatDate(start, end) { - start_date = new Date(start); - end_date = new Date(end); - - day = start_date.getDate(); - month = start_date.getMonth() + 1; - year = start_date.getFullYear(); - - start_hour = start_date.getHours(); - start_mins = start_date.getMinutes(); - - end_hour = end_date.getHours(); - end_mins = end_date.getMinutes(); - - date = day + "/" + month + "/" + year + " "; - date += ("0"+start_hour).slice(-2) + ":" + ("0"+start_mins).slice(-2) + " - " + - ('0' + end_hour).slice(-2) + ":" + ( "0" + end_mins).slice(-2); - return date; -} - -function getGenres() { - $.ajax({ - url: "http://www.bbc.co.uk/tv/programmes/genres.json" - }).done(function(data) { - console.log(data); - data.categories.forEach(function(item) { - $('#genres').append("<li id='"+ item.key +"'>" + item.title + "</li>"); - }) - }).fail(function() { - console.log("something went wrong"); - }); -} - -function getTomorrowsSchedule(genre) { - $.ajax({ - url: "http://www.bbc.co.uk/tv/programmes/genres/"+genre+"/schedules/tomorrow.json", - dataType: 'json', - beforeSend: function() { - $("#programmes").empty(); - $("#programmes").append("<div class='spinner'><img src='spinner.gif' /></div>"); - } - }).done(function(data) { - $(".spinner").remove(); - if (data.broadcasts.length > 0) { - data.broadcasts.forEach(function(episode) { - $("#programmes").append(processEpisode(episode)); - }) - } else { - $("#programmes").append("<div class='no-programmes'>No programmes under " + genre + "</div>"); - } - }).fail(function() { - console.log("something went wrong"); - }); -} - -function getUpcomingEpisodes(pid) { - $.ajax({ - url: "http://www.bbc.co.uk/programmes/" + pid + "/episodes/upcoming.json", - beforeSend: function() { - $("#programmes").empty(); - $("#programmes").append("<div class='spinner'><img src='spinner.gif' /></div>"); - } - }).done(function(data) { - $(".spinner").remove(); - - data.broadcasts.forEach(function(episode) { - $("#programmes").append(processEpisode(episode)); - }) - }).fail(function() { - console.log("something went wrong"); - }); -} - -function processEpisode(episode) { - item_html = "<li><h2>" + episode.programme.display_titles.title + "</h2>"; - item_html += "<h3>" + episode.programme.short_synopsis + "</h3>"; - - if (episode.programme.image) { - item_html += "<img src=http://ichef.bbci.co.uk/images/ic/272x153/"+ episode.programme.image.pid +".jpg />"; - } else { - item_html += "<img src='http://placehold.it/272x153' />"; - } - - item_html += "<p>" + formatDate(episode.start, episode.end)+ "</p>"; - item_html += "<p> <strong>Duration: </strong>" + episode.duration/60 + " minutes</p>"; - - if (episode.programme.position) { - pid = episode.programme.programme.pid; - item_html += "<a class='view-more' id=" + pid +" href='#'> View all upcoming " + episode.programme.display_titles.title + "</a>"; - } - - item_html += "<span class='service'>" + episode.service.title + "</span></li>"; - - return item_html; -} - - -$(document).ready(function(){ - $(document).on('click', '#genres li', function(e){ - genre = $(this).attr('id'); - $("#genres li").removeClass('active'); - $(this).addClass('active'); - - getTomorrowsSchedule(genre); - }) - - $(document).on('click', '.view-more', function(e){ - pid = $(this).attr('id'); - - getUpcomingEpisodes(pid); - }) - - getGenres(); -}); diff --git a/examples/tv-schedule/style.css b/examples/tv-schedule/style.css deleted file mode 100644 index 805fbb5e..00000000 --- a/examples/tv-schedule/style.css +++ /dev/null @@ -1,110 +0,0 @@ -body { - font-family: Helvetica, Arial, sans-serif; - border-top: 5px solid #4c4066; - margin: 0px auto; - font-size: 14px; - color: #666666; - font-size: 16px; -} - -header { - text-align: center; - background-color: #fdfdfd; - border-bottom: 1px solid #e5e2e0; -} - -#logo { - position: relative; - top: -2px; -} - -#container { - width: 1040px; - margin: 0px auto; - min-height: 750px; -} - -input[type='text'] { - height: 2.2em; - font-size: 2em; - width: 400px; - padding: 5px; -} - -.information, .avatar { - vertical-align: top; - display: inline-block; - width: 40%; -} - -.search { - text-align: center; - margin: 50px auto; -} - -h2 { - color: #A26CD2; -} - -ul { - list-style: none; - margin: 0; - padding: 0; -} - -li { - display: inline-block; -} - -#genres li { - padding: 10px; - margin: 5px; - background-color: #5E5A6B; - color: #ffffff;; - border-radius: 5px; -} - -#genres li.active { - background-color: purple; -} - -#programmes li { - width: 45%; - margin: 10px; - padding: 10px; - background-color: #FDFDFD; - border-radius: 5px; - box-shadow: 0px -1px 1px 2px #dbdbdb; - vertical-align: top; -} - -.service { - background-color: #A26CD2; - padding: 5px; - float: right; - border-radius: 5px; - color: black; -} - -.spinner { - width: 100%; - margin-top: 200px; - text-align: center; -} - -.no-programmes { - margin-top: 100px; - text-align: center; -} - -footer { - padding-bottom: 10px; - border-bottom: 5px solid #4c4066; - position: absolute; - width: 100%; -} - -footer #content { - width: 1040px; - margin: 0px auto; -} diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index 5331d3db..a9dad469 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -13,13 +13,14 @@ In this tutorial we are going to look at: * JSON * Loading API data into web pages * Using jQuery AJAX functionality +* Using the fetch() API ### Goal By the end of this tutorial you will have built: * A webpage that can retrieve information about a specified GitHub user -* A webpage that can show the upcoming schedule for BBC shows +* A webpage that can show random generated quiz questions # HTTP Requests @@ -227,15 +228,15 @@ Well done, you've finished! For a bonus, switch your `getGithubInfo` method to r > Coach... explain the difference between synchronous and asynchronous requests. There's a good explanation on [Mozilla Developer Network (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests) -## ~~Exercise 2 - Trivia!~~ +## Exercise 2 - Trivia! -For this exercise, we will build a web app that generates trivia questions, using this open trivia questions api: https://opentdb.com/api_config.php +For this exercise, we will build a web app that generates trivia questions, using this open trivia questions api: https://opentdb.com/api_config.php. [Download](https://gist.github.com/kyorkston/a2768287fe15fc693da3dc62e8a8d697/download) the exercise files or clone them directly from github `git clone https://gist.github.com/a2768287fe15fc693da3dc62e8a8d697.git`. Shoutout to [despo](https://gist.github.com/despo) for originially making these files! ### What we will be doing: -1. Retrieve and log quiz questions `https://opentdb.com/api.php?amount=10` +1. Retrieve and log quiz questions usinf the quiz API: `https://opentdb.com/api.php?amount=10` 2. Write a function that displays each question @@ -243,11 +244,11 @@ For this exercise, we will build a web app that generates trivia questions, usin ### Retrieve and render log questions -Firstly, here is an introduction to fetch() from Google - [Introduction to fetch() by Matt Gaunt](https://developers.google.com/web/updates/2015/03/introduction-to-fetch) +Firstly, here is an introduction to the fetch() Web API - [Introduction to fetch() by Matt Gaunt](https://developers.google.com/web/updates/2015/03/introduction-to-fetch) -There is a whole lot of jargon in there but what the fetch() function does is simplify things for the developer when making a request to an api. +There is a whole lot of jargon in there but what the fetch() function does is simplify things for the developer when making a request to an API. -Lets first create the fetch request to the trivia api: +Lets first create the fetch request to the trivia API: ```js fetch('https://opentdb.com/api.php?amount=10') @@ -257,18 +258,17 @@ fetch('https://opentdb.com/api.php?amount=10') .then(function(data) { //work with the json response data here console.log(data) - }) }) ``` -We send a request with `fetch`, returning a Promise to us in the form of the response. This response is passed into a -`then()` function (a Promise) where we transform it into something readable - JSON. When that Promise is resolved _then_ (`then()`) we can use this `response.json()` how we want to use it in our code when that Promise is resolved (a `console.log()` in this example). -For more information on Promises and asynchronous functions check out [javascript.info/promise-basics](https://javascript.info/promise-basics). But we're here to play with APIs so let's get on with the tutorial. +We send a request with `fetch`, the reposnse returns a Promise. This response is passed into a +`then()` function (a Promise) where we transform it into something readable and usable - a JSON object. When that Promise resolves _then_ (`then()`) we can use the response (`response.json()`) however we want to use it in our code when that Promise is resolved - in our case a `console.log()`. +For more information on Promises and asynchronous functions check out [javascript.info/promise-basics](https://javascript.info/promise-basics). Now open the `index.html` file in a web browser and then inspect the page - here is an article on how to do so in every browser: [How to Inspect Web Page Elements](https://www.lifewire.com/get-inspect-element-tool-for-browser-756549). -Once you have done so a panel will appear and it will show you the html elements on your page. In this panel there is a 'Console' tab, click this and see what has been logged. +Once you have inspected the page, a panel will appear and it will show you the html elements on your page. In this panel there is a 'Console' tab, click this and see what has been logged. -You should see something that looks like this: +You should see printed out something that looks like this: ```js { "response_code": 0, @@ -313,7 +313,10 @@ function fetchQuizQuestions() { ### Write a function that displays each question -Now we need to render the questions onto our webpage. We will add them in a list format, for that we use the <li> html tag. This will be going inside the <ul> tag that we have in `index.html`, it has the id "questions". Refer back to Lesson 2 on how to create html elements and add data to them. +Now we need to render the questions onto our webpage. We will add them in a list format, for that we use the `<li>` html tag. This will be going inside the `<ul>` tag that we have in `index.html`, it has the id "questions". Refer back to [Javascript Lesson 2](tutorials.codebar.io/js/lesson2/tutorial.html) on how to create html elements and add data to them. + + +As the results come back in the form of an array we can run `results.map()` over the array and access the properties inside the result object. ```js function renderQuizQuestions() { @@ -321,7 +324,7 @@ function renderQuizQuestions() { } ``` -Put this into the second `then()` (Promise) of your fetch function, something like: +Put this into the second part of the Promise (`then()`) of your fetching function, something like: ```js fetch(...) @@ -331,6 +334,8 @@ fetch(...) }) ``` +To run your code you just have to call our fetch function like `fetchQuizQuestions()` at the bottom of the index.js file. + Because fetch() is an asynchronous function the rest of the code in your file could run at the same time as the fetch function, meaning that the fetched data may not appear if used in an outside function, for example: @@ -344,10 +349,11 @@ function renderQuizQuestions() { } ``` -The `data` inside `renderQuizQuestions()` will come back as undefined as it is run before the asynchronous function has resolved. +The `data` inside `renderQuizQuestions()` will come back as undefined as it is run before the asynchronous function has resolved/finished. So now you should hopefully have some Questions rendered onto your webpage, if not please ask for help from a Codebar coach, a fellow Codebar attendee or checkout the Codebar Slack chatrooms. + ### **BONUS** Play around with the API URL generator The trivia api we're using is pretty cool and you can configure it in a few ways, check it out: [https://opentdb.com/api_config.php](https://opentdb.com/api_config.php). @@ -358,10 +364,29 @@ If you select a few of the options and then generate an api, you could get somet We have `amount=20`, this is the amount of questions. `category=10` this is the book category, so each category is mapped to a number in the API. And we have `difficulty=medium`, setting the difficulty of the questions. You can play around with this a lot and its a simple interface and you can make the questions as easy or as difficult as you want! -Now that you can render the questions how about now rendering the answers? -Perhaps you could have a button to reveal the answer? -Or if you select the multi-choice questions you could add some fun css to let the user know if they got the answer right or wrong! +Let's create a selector to choose what difficulty of questions we would like. +There is 3 difficulty settings `['easy', 'medium', 'hard']` which we can create a html selector for. +We would then inject the selected value into our `fetchQuizQuestions()` function. + +Create a function where we can loop through the list of difficulties, creating an `<option>` for each one and adding the options value. +Then append this list of options to a `<select>` element. + +Now add an event listener to the selector's `'change'` event. This will be triggered whenever a user selects a new option. In the listener we want to pass the value of the event into our `fetchQuizQuestions` function. +We can expand the API call to look like this: + +```js +fetchQuizQuestions(difficulty) { + return fetch(`https://opentdb.com/api.php?amount=10&difficulty=${difficulty}`) +``` + +You've now got the tools to play around with this API as much as you want, where you can create an amazing quiz app! + +Here is our version of the [trivia quiz app](../../examples/trivia-quiz/index.html). + +Now that you can render the questions and the user can change the difficulty setting, how about now rendering the answers? +Perhaps you could have a button to reveal the answer? +Or if you select the multi-choice questions you could add some fun CSS styles to let the user know if they got the answer right or wrong! --- This ends our **HTTP Requests, AJAX and APIs** tutorial. Is there something you don't understand? Try and go through the provided resources with your coach. If you have any feedback, or can think of ways to improve this tutorial [send us an email](mailto:feedback@codebar.io) and let us know. From df126753cc18de64c7ad74fde2917f0330ffcd72 Mon Sep 17 00:00:00 2001 From: kyorkston <kyorkston@gmail.com> Date: Fri, 31 Jan 2020 14:57:44 +0000 Subject: [PATCH 5/6] add catch to fetch function --- examples/trivia-quiz/index.js | 9 ++++++--- examples/trivia-quiz/{style.css => styles.css} | 0 js/lesson4/tutorial.md | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) rename examples/trivia-quiz/{style.css => styles.css} (100%) diff --git a/examples/trivia-quiz/index.js b/examples/trivia-quiz/index.js index 7644485d..090cf5a5 100644 --- a/examples/trivia-quiz/index.js +++ b/examples/trivia-quiz/index.js @@ -1,12 +1,15 @@ // As fetch() is an asynchronous function we can add the async/await keywords async function fetchQuizQuestions(difficulty) { return await fetch(`https://opentdb.com/api.php?amount=10&difficulty=${difficulty}`) - .then((response) => { + .then(response => { return response.json() }) - .then((data) => { + .then(data => { renderQuizQuestions(data) - }) + }) + .catch(error => { + console.error('Error: ', error) + }) } function renderQuizQuestions(data) { diff --git a/examples/trivia-quiz/style.css b/examples/trivia-quiz/styles.css similarity index 100% rename from examples/trivia-quiz/style.css rename to examples/trivia-quiz/styles.css diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index a9dad469..aff40e3e 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -263,6 +263,24 @@ fetch('https://opentdb.com/api.php?amount=10') ``` We send a request with `fetch`, the reposnse returns a Promise. This response is passed into a `then()` function (a Promise) where we transform it into something readable and usable - a JSON object. When that Promise resolves _then_ (`then()`) we can use the response (`response.json()`) however we want to use it in our code when that Promise is resolved - in our case a `console.log()`. + +Since things can happen with API responses and its not always a 200 OK status, we can add a `.catch` to the fetch so we can log any issues when fetching data from the api. + +```js +fetch('https://opentdb.com/api.php?amount=10') + .then(function(response) { + return response.json() + }) + .then(function(data) { + //work with the json response data here + console.log(data) + }) + .catch(function(error) { + console.error('Error: ', error) + }) + +``` + For more information on Promises and asynchronous functions check out [javascript.info/promise-basics](https://javascript.info/promise-basics). Now open the `index.html` file in a web browser and then inspect the page - here is an article on how to do so in every browser: [How to Inspect Web Page Elements](https://www.lifewire.com/get-inspect-element-tool-for-browser-756549). From 12b0093496c4cbbfc3d923bd89bf0d1cddbf168c Mon Sep 17 00:00:00 2001 From: Kristie Yorkston <kyorkston@gmail.com> Date: Fri, 2 Oct 2020 08:58:11 +0100 Subject: [PATCH 6/6] Fix typo Co-authored-by: Kristian Hamilton <kristian.hamilton@signavio.com> --- js/lesson4/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/lesson4/tutorial.md b/js/lesson4/tutorial.md index aff40e3e..1e212ab9 100644 --- a/js/lesson4/tutorial.md +++ b/js/lesson4/tutorial.md @@ -236,7 +236,7 @@ For this exercise, we will build a web app that generates trivia questions, usin ### What we will be doing: -1. Retrieve and log quiz questions usinf the quiz API: `https://opentdb.com/api.php?amount=10` +1. Retrieve and log quiz questions using the quiz API: `https://opentdb.com/api.php?amount=10` 2. Write a function that displays each question