Как да създадете генератор на произволни ястия

Миналата седмица реших да се справя с ново предизвикателство. Нарекох го: Предизвикателството # 100Days100Projects.

Целта на предизвикателството е да се създаде един проект всеки ден. Помислете за това като за следваща стъпка за предизвикателството # 100DaysOfCode.

Проектът може да бъде:

  • приложение
  • компонент
  • уебсайт
  • игра
  • библиотека

    и така нататък...

Използваният език за програмиране също не е важен, но трябва да завърша проекта до 23:59 ч. (По мое време), в противен случай се „наказвам“, като раздавам 5 долара за 5 души (общо 25 долара) - първите 5 души, които посочете в Twitter, че съм пропуснал крайния срок. ?

Ако искате да се присъедините, можете да прочетете повече за това предизвикателство и другите варианти, които то има тук.

Забележка : не е нужно да раздавате 5 долара, ако не успеете, просто задайте друго „наказание“ за себе си. Освен това има и други варианти с по-малко дни ( 7Days7Projects и 30Days30Projects ), ако не ви се иска да се справите с предизвикателството 100Days.

За първия проект в # 100Days100Projects мислех за работа с публичен API, за да получа някои данни, които ще се показват в уеб страница - обичайно нещо, което трябва да се направи с API.

За това избрах да използвам публичния API на TheMealDB, за да получа някои произволни ястия чрез натискане на бутон. Нещо директно! ?

Вижте актуалната версия на това, което ще изградим в тази статия в CodePen:

Както винаги нека започнем в началото:

HTML

Feeling hungry?

Get a random meal by clicking below
Get Meal ?

Имаме малко текст, но двете най-важни части са:

  • на #get_mealбутона и
  • на #mealDIV

Ще използваме, за buttonда отправим заявка към API. Това ще изпрати обратно някои данни, които ще въведем в #mealdiv, който действа като контейнер - в този случай.

Обикновено след HTML ще отида направо в CSS. Но все още нямаме цялата маркировка, тъй като тя ще бъде попълнена в раздела JavaScript , така че това ще направим по-нататък.

JavaScript

Както бе споменато по-горе, ние се нуждаем от buttonи този контейнер div:

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

След това, преди да се потопим повече в кода, нека видим какво ще върне API. За това, моля, отворете следния URL: //www.themealdb.com/api/json/v1/1/random.php.

Както можете да видите от URL адреса, получаваме произволно хранене от този API (опресняване, за да видите произволността ). Когато отправяме GET заявка към тази крайна точка (като достъп до нея от браузъра), тя изпраща обратно JSON отговор, който можем да анализираме, за да извлечем данните, които искаме.

Данните изглеждат по следния начин:

{ 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 } ]; } 

По принцип получаваме обратно масив от meals, но само с един елемент в него - случайно генерирания. И този елемент има всички данни, които искаме да покажем в нашето малко приложение. Неща като:

  • име на хранене (под strMeal)
  • хранене катерогия (под strCategory)
  • изображение на хранене (под strMealThumb)
  • видео в YouTube с рецептата (под strYoutube)
  • съставките и мерките (под strIngredientsXи strMeasureX- X, представляващи n-тата съставка и нейната мярка). Това е малко неудобно, тъй като бих очаквал тук да има масив с тази информация, но те избират да го добавят като обектни подпори. На добре ...? Важното е да се отбележи, че има максимум 20 съставки / мерки, въпреки че не всички са попълнени - някои от тях може да са празни, така че трябва да отчетем това.

Сега, когато имаме бутона, ще добавим слушател на clickсъбитие за събитието. Вътре ще отправим заявка към 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); }); }); 

Използваме API за извличане, за да направим заявката. Просто трябва да предадем URL адреса на API, към който искаме да направим GET заявка, и ще получим обратно обещание.

След като това бъде решено, имаме отговор ( res). Това resвсе още не е в състоянието, в което искаме да бъде, така че ще извикаме .json()метода върху него. Тогава най-накрая имаме красивия обект. Ех! ?

Както бе споменато по-горе, API връща mealsмасива, но само с елемент в него. Така че ще предадем този елемент (в индекс 0) в нашата createMealфункция, която ще дефинираме по-нататък.

Ще поставя целия блок от код по-долу и след това ще влезем в подробности, така че задръжте за секунда. ?

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; };

По същество целта на цялата функция е да получи JSON отговора, да го анализира и преобразува в HTML компонент. За това трябва да направим няколко неща, тъй като данните все още не са оформени точно по начина, по който искаме да бъдат.

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

Original text


  • 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! ?