Sådan oprettes en tilfældig måltidsgenerator

Sidste uge besluttede jeg at tage en ny udfordring på. Jeg kaldte det: # 100Days100Projects Challenge.

Formålet med udfordringen er at oprette et projekt hver eneste dag. Tænk på det som et næste trin til # 100DaysOfCode-udfordringen.

Et projekt kan være enten:

  • en app
  • en komponent
  • et websted
  • et spil
  • et bibliotek

    og så videre...

Det anvendte programmeringssprog er heller ikke vigtigt, men jeg har brug for at afslutte projektet kl. 23:59 (min tid), ellers straffer jeg mig selv ved at give $ 5 til 5 personer ($ 25 i alt) - først 5 personer, der påpeg det på Twitter, at jeg savnede deadline. ?

Hvis du vil være med, kan du læse mere om denne udfordring og de andre varianter, den har her.

Bemærk : du behøver ikke give $ 5 væk, hvis du fejler, bare sæt en anden "straf" til dig selv. Der er også andre varianter med færre dage ( 7Days7Projects og 30Days30Projects ), hvis du ikke har lyst til at tage 100Days-udfordringen på.

Til det første projekt i # 100Days100Projects tænkte jeg på at arbejde med en offentlig API for at få nogle data, der ville blive vist på en webside - en almindelig ting at gøre med en API.

Til det valgte jeg at bruge TheMealDB's offentlige API for at få nogle tilfældige måltider ved at trykke på en knap. Noget ligetil! ?

Tjek den live version af hvad vi skal bygge i denne artikel på CodePen:

Lad os som altid starte i starten:

HTML

Feeling hungry?

Get a random meal by clicking below
Get Meal ?

Vi har en lille tekst, men de to vigtigste dele er:

  • #get_mealknappen og
  • den #mealdiv

Vi bruger den buttontil at stille en anmodning til API'et. Dette sender nogle data tilbage, som vi vil lægge i #mealdiv, der fungerer som en container - i dette tilfælde.

Normalt efter HTML går jeg lige ind i CSS. Men vi har endnu ikke hele markeringen, da den udfyldes i JavaScript- sektionen, så det er hvad vi skal gøre næste.

JavaScript

Som nævnt ovenfor har vi brug for den buttonog den container div:

const get_meal_btn = document.getElementById('get_meal'); const meal_container = document.getElementById('meal'); 

Dernæst, inden vi dykker mere ind i koden, skal vi se, hvad API'en vil returnere. For det skal du åbne følgende URL: //www.themealdb.com/api/json/v1/1/random.php.

Som du kan se fra URL'en, får vi et tilfældigt måltid fra denne API (opdater for at se tilfældigheden ). Når vi fremsætter en GET- anmodning til dette slutpunkt (som at få adgang til den fra browseren), sender den et JSON-svar tilbage, som vi kan analysere for at hente de data, vi ønsker.

Dataene ser sådan ud:

{ meals: [ { idMeal: '52873', strMeal: 'Beef Dumpling Stew', strDrinkAlternate: null, strCategory: 'Beef', strArea: 'British', strInstructions: 'Long description', strMealThumb: '//www.themealdb.com/images/media/meals/uyqrrv1511553350.jpg', strTags: 'Stew,Baking', strYoutube: '//www.youtube.com/watch?v=6NgheY-r5t0', strIngredient1: 'Olive Oil', strIngredient2: 'Butter', strIngredient3: 'Beef', strIngredient4: 'Plain Flour', strIngredient5: 'Garlic', strIngredient6: 'Onions', strIngredient7: 'Celery', strIngredient8: 'Carrots', strIngredient9: 'Leek', strIngredient10: 'Swede', strIngredient11: 'Red Wine', strIngredient12: 'Beef Stock', strIngredient13: 'Bay Leaf', strIngredient14: 'Thyme', strIngredient15: 'Parsley', strIngredient16: 'Plain Flour', strIngredient17: 'Baking Powder', strIngredient18: 'Suet', strIngredient19: 'Water', strIngredient20: '', strMeasure1: '2 tbs', strMeasure2: '25g', strMeasure3: '750g', strMeasure4: '2 tblsp ', strMeasure5: '2 cloves minced', strMeasure6: '175g', strMeasure7: '150g', strMeasure8: '150g', strMeasure9: '2 chopped', strMeasure10: '200g', strMeasure11: '150ml', strMeasure12: '500g', strMeasure13: '2', strMeasure14: '3 tbs', strMeasure15: '3 tblsp chopped', strMeasure16: '125g', strMeasure17: '1 tsp ', strMeasure18: '60g', strMeasure19: 'Splash', strMeasure20: '', strSource: '//www.bbc.co.uk/food/recipes/beefstewwithdumpling_87333', dateModified: null } ]; } 

Dybest set får vi tilbage en række meals, men med kun en vare i den - den tilfældigt genererede. Og denne vare har alle de data, vi vil fremvise i vores lille applikation. Ting som:

  • måltid navn (under strMeal)
  • måltidskatogogi (under strCategory)
  • måltid billede (under strMealThumb)
  • en youtube-video med opskriften (under strYoutube)
  • ingredienserne og målene (under strIngredientsXog strMeasureX- X repræsenterer den nende ingrediens og dens mål). Dette er lidt akavet, da jeg her forventer at have en matrix med denne information, men de vælger at tilføje den som objektrekvisitter. Godt ...? Den vigtige ting at bemærke er, at der er maksimalt 20 ingredienser / tiltag, selvom de ikke alle er udfyldt - nogle af dem kan være tomme, så vi skal redegøre for det.

Nu hvor vi har knappen, tilføjer vi en begivenhedslytter til clickbegivenheden. Inde vil vi stille en anmodning til API:

get_meal_btn.addEventListener('click', () => { fetch('//www.themealdb.com/api/json/v1/1/random.php') .then(res => res.json()) .then(res => { createMeal(res.meals[0]); }) .catch(e => { console.warn(e); }); }); 

Vi bruger hente API til at udføre anmodningen. Vi er bare nødt til at videregive url til den API, vi ønsker at indgive en GET- anmodning til, og vi får et løfte tilbage.

Når dette er løst, har vi et svar ( res). Dette reser endnu ikke i den tilstand, vi vil have det, så vi kalder .json()metoden på det. Så endelig har vi det smukke objekt. Yay! ?

Som nævnt ovenfor returnerer API'et mealsarrayet, men kun med et element i det. Så vi sender dette element (i indeks 0) til vores createMealfunktion, som vi definerer derefter.

Jeg vil indsætte hele blokken kode nedenfor, og vi vil gå i detaljer bagefter, så hold et øjeblik. ?

const createMeal = meal => { const ingredients = []; // Get all ingredients from the object. Up to 20 for (let i = 1; i <= 20; i++) { if (meal[`strIngredient${i}`]) { ingredients.push( `${meal[`strIngredient${i}`]} - ${meal[`strMeasure${i}`]}` ); } else { // Stop if there are no more ingredients break; } } const newInnerHTML = `  ${ meal.strCategory ? `

Category: ${meal.strCategory}

` : '' } ${meal.strArea ? `

Area: ${meal.strArea}

` : ''} ${ meal.strTags ? `

Tags: ${meal.strTags .split(',') .join(', ')}

` : '' }
Ingredients:
    ${ingredients.map(ingredient => `
  • ${ingredient}
  • `).join('')}

${meal.strMeal}

${meal.strInstructions}

${ meal.strYoutube ? `
Video Recipe
` : '' } `; meal_container.innerHTML = newInnerHTML; };

Dybest set er hele funktionens formål at få JSON-svaret, analysere det og omdanne det til en HTML-komponent. Til det er vi nødt til at gøre et par ting, da dataene endnu ikke er formuleret nøjagtigt som vi vil have det.

First, we're getting all the ingredients and their measures. As mentioned above there are a maximum of 20 ingredients, but they are separated into their own properties in the object like: strIngredient1, strIngredient2, etc... (I still don't know why they did that, but... ?).

So, we're creating a for loop which goes from 1 to 20 and checks if the meal has that corresponding ingredient-measure pair. If it does, we're putting it into the ingredients array. If there aren't any more ingredients we're stopping the for loop with a break condition.

Next, we're creating the newInnerHTML string which is going to hold the entire HTML markup. In it we are parsing the remaining properties that we want to be displayed.

Note that some of the properties might not be available. So for that we're using the ternary operator to check if we have the data to display the corresponding tag. If we don't have it then we're returning an empty string and nothing will be displayed on the page. The category and the area are examples of these type of properties.

The tags are coming in a string divided by a comma like: 'tag1,tag2,tag3'. So we need to split it by that comma, and join it back by a comma and a space as it looks nicer ('tag1, tag2, tag3' ❤️). Or at least for me does. ?

To show the ingredients, we're mapping over the array and we're creating an

  • for each ingredient/measure pair. At the end we're joining the array back to form a string. (This is something you would do in ReactJS but without the joining part ?).

    There is also a Youtube video string (maybe) which is returning the URL of the video. But in order for us to embed the video in the page we need to extract the video ID only. For that we're using .slice(-11) to get the last 11 characters of the string as this is where the ID is hiding ?.

    And finally, we're setting this entire newInnerHTML to be the meal_container's innerHTML -> this will populate that div with all this information!

    This entire process will repeat every time we're pressing the Get Meal button.

    The CSS

    The last part is to style it a little bit, right? ?

    For the CSS I wanted to use something new so I tried out the SkeletonCSS library. It's useful if you have a small project and don't want to get overwhelmed with all those classes, as it only has a couple of them that take care of some basic styling (the button for example) and the responsive part.

    @import url('//fonts.googleapis.com/css?family=Muli&display=swap'); * { box-sizing: border-box; } body { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 30px 0; min-height: calc(100vh - 60px); } img { max-width: 100%; } p { margin-bottom: 5px; } h3 { margin: 0; } h5 { margin: 10px 0; } li { margin-bottom: 0; } .meal { margin: 20px 0; } .text-center { text-align: center; } .videoWrapper { position: relative; padding-bottom: 56.25%; padding-top: 25px; height: 0; } .videoWrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 

    You can see that the CSS is pretty simple. The only part that's worth mentioning is the .videoWrapper CSS declaration. This makes sure that the YouTube embed is responsive. (Got this from CSS-Tricks - thanks guys! ?)

    Conclusion

    And voilà! We're done! ?

    You should now know how to use a public API to get some data which you can then insert on the page easily! Well done! ?

    This is the first project I did for the #100Days100Projects challenge. You can check out what other projects I've built and what are the rules of the challenge (if you might want to join) by clicking here.

    You can read more of my articles on www.florin-pop.com.

    Happy Coding! ?