Cómo crear un generador de comidas al azar

La semana pasada decidí asumir un nuevo desafío. Lo llamé: El desafío # 100Days100Projects.

El propósito del desafío es crear un proyecto todos los días. Piense en ello como el siguiente paso del desafío # 100DaysOfCode.

Un proyecto puede ser:

  • Una aplicación
  • un componente
  • una página web
  • un juego
  • una biblioteca

    y así...

El lenguaje de programación utilizado tampoco es importante, pero necesito completar el proyecto antes de las 11:59 p.m. (mi hora); de lo contrario, me estoy "castigando" regalando $ 5 para 5 personas ($ 25 en total), las primeras 5 señalar en Twitter que no cumplí con la fecha límite. ?

Si quieres unirte, puedes leer más sobre este desafío y las otras variantes que tiene aquí.

Nota : no tienes que regalar $ 5 si fallas, solo establece algún otro "castigo" para ti. Además, hay otras variantes con menos días ( 7Days7Projects y 30Days30Projects ) si no te apetece asumir el desafío de 100Days.

Para el primer proyecto en # 100Days100Projects, pensé en trabajar con una API pública para obtener algunos datos que se mostrarían en una página web, algo habitual en una API.

Para eso, elegí usar la API pública de TheMealDB para obtener algunas comidas al azar presionando un botón. ¡Algo sencillo! ?

Vea la versión en vivo de lo que vamos a construir en este artículo sobre CodePen:

Como siempre, comencemos por el principio:

El HTML

Feeling hungry?

Get a random meal by clicking below
Get Meal ?

Tenemos un pequeño texto, pero las dos partes más importantes son:

  • el #get_mealbotón y
  • el #mealdiv

Usaremos buttonpara hacer una solicitud a la API. Esto enviará algunos datos que colocaremos en el #mealdiv que actúa como un contenedor, en este caso.

Por lo general, después del HTML, iré directamente al CSS. Pero todavía no tenemos el marcado completo, ya que se completará en la sección de JavaScript , así que eso es lo que haremos a continuación.

El JavaScript

Como se mencionó anteriormente, necesitamos el buttony ese contenedor div:

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

A continuación, antes de profundizar más en el código, veamos qué devolverá la API. Para eso, abra la siguiente URL: //www.themealdb.com/api/json/v1/1/random.php.

Como puede ver en la URL, estamos obteniendo una comida aleatoria de esta API (actualice para ver la aleatoriedad ). Cuando hacemos una solicitud GET a ese punto final (como acceder a él desde el navegador), envía una respuesta JSON, que podemos analizar para recuperar los datos que queremos.

Los datos se parecen a esto:

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

Básicamente, obtenemos una matriz meals, pero con solo un elemento: el generado aleatoriamente. Y este artículo tiene todos los datos que queremos mostrar en nuestra pequeña aplicación. Cosas como:

  • nombre de la comida (debajo strMeal)
  • categoría de comida (debajo strCategory)
  • imagen de comida (debajo strMealThumb)
  • un video de youtube con la receta (debajo strYoutube)
  • los ingredientes y las medidas (debajo strIngredientsXy strMeasureX- X que representa el enésimo ingrediente y su medida). Esto es un poco incómodo, ya que esperaría que aquí tuviera una matriz con esta información, pero eligen agregarla como accesorios de objeto. Bien... ? Lo importante a tener en cuenta es que hay un máximo de 20 ingredientes / medidas, aunque no están todos completados; algunos de ellos pueden estar vacíos, por lo que debemos tener en cuenta eso.

Ahora que tenemos el botón, agregaremos un detector de eventos para el clickevento. En el interior vamos a hacer una solicitud a la 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); }); }); 

Estamos usando la API de recuperación para realizar la solicitud. Solo tenemos que pasar la URL de la API a la que queremos realizar una solicitud GET y obtendremos una promesa.

Una vez que esto se resuelve, tenemos una respuesta ( res). Esto resaún no está en el estado que queremos, así que vamos a llamar al .json()método. Entonces finalmente tenemos el objeto hermoso. ¡Hurra! ?

Como se mencionó anteriormente, la API devuelve la mealsmatriz, pero solo con un elemento. Así que vamos a pasar ese elemento (en el índice 0) a nuestra createMealfunción, que definiremos a continuación.

Voy a pegar todo el bloque de código a continuación y luego entraremos en detalles, así que espere un segundo. ?

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

Básicamente, el propósito de toda la función es obtener la respuesta JSON, analizarla y transformarla en un componente HTML. Para eso, debemos hacer un par de cosas, ya que los datos aún no están formateados exactamente de la manera que queremos.

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