Juneikerc.com

Como crear un generador de comidas aleatorio con qwik

Imagen destacada del post: Como crear un generador de comidas aleatorio con qwik

Buscando ejercicios para realizar con qwik me encontré con un generador de comidas aleatorios y creo que es una buena forma de explicar algunas de las características de qwik como los routeAction$, sistema de rutas etc etc.

En este Tutorial aprenderás a crear un generador de comidas aleatorio utilizando qwik. Antes de empezar debes saber que para seguir este tutorial necesitas conocer al menos los fundamentos básicos del funcionamiento de qwik.

Explicación del proyecto

Como base para este proyecto necesito crear como mínimo dos rutas:

  • La página de inicio: con el botón para generar la comida
  • La página de la receta: esta página debe ser dinámica y donde irá toda la información de la comida.

Usaré para obtener toda esta información la API themealdb

Nota importante

Se pueden agregar más características como obtener comidas por categoría o áre,a pero para fines de este tutorial solo me centraré en lo más básico.

Creando la página de inicio y un routeAction$

tsx
import { component$ } from "@builder.io/qwik";
import { routeAction$, type DocumentHead } from "@builder.io/qwik-city";
export const useRandomMealAction = routeAction$(async (_, { redirect }) => {
const response = await fetch(
"https://www.themealdb.com/api/json/v1/1/random.php"
);
const data = await response.json();
const randomMeal = data.meals[0];
const mealRoute = randomMeal.strMeal
.replace(/[^\w\s]/g, "")
.replaceAll(" ", "-")
.toLowerCase();
redirect(302, `/${mealRoute}`);
});
export default component$(() => {
const action = useRandomMealAction();
return (
<main>
<section>
<h1>Click on button to generate random meal</h1>
<h2>You don't know what to eat?</h2>
<p>Use our Random meal Generator</p>
<button
onClick$={async () => {
await action.submit();
}}
>
Generate meal
<svg
xmlns="http://www.w3.org/2000/svg"
class="icon icon-tabler icon-tabler-wand"
width="20"
height="20"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="#2c3e50"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M6 21l15 -15l-3 -3l-15 15l3 3" />
<path d="M15 6l3 3" />
<path d="M9 3a2 2 0 0 0 2 2a2 2 0 0 0 -2 2a2 2 0 0 0 -2 -2a2 2 0 0 0 2 -2" />
<path d="M19 13a2 2 0 0 0 2 2a2 2 0 0 0 -2 2a2 2 0 0 0 -2 -2a2 2 0 0 0 2 -2" />
</svg>
</button>
</section>
</main>
);
});
export const head: DocumentHead = {
title: "Welcome to Qwik",
meta: [
{
name: "description",
content: "Qwik site description",
},
],
};

Las acciones routeAction$ se utilizan para manejar el envió de formularios permitiendo realizar efectos secundarios como escribir en una base de datos o enviar un correo electrónico.

Para este caso necesitamos que al hacer click sobre un botón se dispare una acción para llamar a una API que devolverá una receta al azar y una vez obtenida la receta redireccionar a una URL dinámica que contendrá todos los datos de la comida.

Antes de hacer la redirección le damos formato a el nombre de la receta para que pueda ser una URL válida.

js
const mealRoute = randomMeal.strMeal
.replace(/[^\w\s]/g, "")
.replaceAll(" ", "-")
.toLowerCase();

Obtener los datos de cada comida con routeLoader$

La manera recomenda de obtener los datos para el primer renderizado en qwik es a través del método routeLoader$.

Qwik routeLoader para obtener los datos de la receta

tsx
import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
export const useGetMealData = routeLoader$(async ({ params }) => {
const response = await fetch(
`https://www.themealdb.com/api/json/v1/1/search.php?s=${params.meal.replaceAll(
"-",
" "
)}`
);
const data = await response.json();
const meal = data.meals[0];
const extractIngredients = extractElementsOfObject(
data.meals[0],
"strIngredient"
);
const extractMeasures = extractElementsOfObject(data.meals[0], "strMeasure");
const ingredients = extractIngredients.map((ingredient, index) => ({
ingredient,
measure: extractMeasures[index],
}));
return {
name: meal.strMeal,
category: meal.strCategory,
area: meal.strArea,
ingredients,
instructions: meal.strInstructions,
thumbnail: meal.strMealThumb,
tags: meal.strTags,
youtube: meal.strYoutube,
source: meal.strSource,
};
});

Funciones auxiliares para formatear la respuesta de la API

ts
export const extractElementsOfObject = (object: any, propertyStr: string) => {
const elements = [];
for (const property in object) {
if (property.includes(propertyStr)) {
elements.push(object[property]);
}
}
return elements.filter((element) => element.trim() !== "");
};

Renderizando el componente para mostrar los datos de la comida generada al azar

tsx
export default component$(() => {
const mealData = useGetMealData().value;
const youtubeVideoId = new URL(mealData.youtube).searchParams.get("v");
return (
<main>
<GoHome />
<section style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
<div>
<img
src={mealData.thumbnail}
alt={mealData.name}
width={300}
height={300}
/>
<h2>Ingredients</h2>
<ul>
{mealData.ingredients.map((ingredient, i) => (
<li key={i}>
{ingredient.ingredient} - {ingredient.measure}
</li>
))}
</ul>
</div>
<div>
<h1>{mealData.name}</h1>
<p>{mealData.instructions}</p>
</div>
</section>
<h2>Video Recipe</h2>
<iframe
width={560}
height={315}
src={`https://www.youtube.com/embed/${youtubeVideoId}`}
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen
></iframe>
</main>
);
});

Espero que este tutorial te haya servido para comprender un poco mejor el funcionamiento de qwik sobre todo de sus funciones routeLoader$ y routeAction$, muchas gracias por leerme.

Juneiker Castillo freelance web developer

Soy Juneiker Castillo, un desarrollador web frontend apasionado por la programación y la creación de sitios web modernos rápidos y escalables, en fin un friki 🤓 de javascript enamorado de react js ⚛️.

Sobre mi