Як створити генератор випадкових страв

Минулого тижня я вирішив прийняти новий виклик. Я назвав це: Виклик # 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кнопка і
  • #mealДІВ

Ми збираємось використати, 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! ?