Creación y uso de componentes
Todos los framework de Javascript están basados en componentes. Los componentes son como piezas de
un puzzle que nos ayudan a reutilizar código de nuestra interfaz. En vez de tener todo el código en un archivo,
creamos componentes que nos ayudan tanto a organizar el código como hacerlo reutilizable, porque después, ese
componente, lo podemos utilizar en todas las partes de la aplicación que queramos.
Para agregar componentes al proyecto, dentro del directorio src creamos una carpeta a la cual vamos
a llamar components. En esta carpeta vamos a ir creando todos nuestros componentes. Por ejemplo,
crearemos un componente que sea la tarjeta de un post para un blog. Llamaremos a este componente
PostCard.astro
Los nombres de los archivos de componentes (archivos en src/components), así como los de las
plantillas o Layouts que se verán más adelante, se nombran con la primera letra de cada palabra en
mayúscula (UpperCamelCase, como las clases de JavaScript). Los componentes de
Astro utilizan la extensión .astro al igual que las páginas.
Estructura de un archivo de Astro
Un archivo .astro se estructura en dos partes principales separadas por tres guiones (---). Estas
partes son:
-
Frontmatter (scripts): Se encuentra en la parte superior del archivo, entre dos bloques de tres guiones (---). Aquí se escribe el código JavaScript (o TypeScript) que se ejecuta en el servidor o durante la compilación. Permite definir variables locales, importar otros componentes y gestionar datos antes del renderizado.
-
Documento HTML: Está situado debajo del segundo bloque de guiones y define la estructura HTML que se mostrará. Si queremos inyectar
Javascripten el HTML, se incluye el código entre llaves { }.Los atributos HTML usan el estándar kebab-case (ejemplo: class, data-value).
Crear un archivo de componente
Vamos a crear la siguiente tarjeta usando la etiqueta <article>. A continuación, dejo el
código (copiarlo).
---
---
<article>
<img src="/img/gato.webp" alt="gatito">
<h2>The Post Title</h2>
<p>A post description</p>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Neque eius distinctio aspernatur incidunt,
tempore expedita architecto est,
sint natus, nihil quaerat nisi.
Saepe pariatur nihil fugit itaque
esse doloribus corporis!
</p>
<p>10/12/2023 - 16 min read</p>
</article>
Dentro colocaremos una imagen. Las imágenes se guardan en el directorio public/img/ (inicialmente
utilizaremos cualquier imagen de prueba).
Utilizar el componente en una página
Para utilizar un componente en una página (en nuestro caso index.astro), lo importamos en la sección
de frontmatter usando el comando import:
import Post from "../components/Post.astro"
Luego, indicamos dónde lo queremos ver. Para mostrarlo, en la sección de HTML, creamos una etiqueta con el nombre
del mismo, en este caso <Post/> (usando la sintaxis de autocierre).
Estilar un componente
Para añadir los estilos al componente Post.astro usaremos tailwind. Una de las
ventajas que tiene Astro es que instalar ciertas herramientas es muy fácil. Para ello, desde la terminal
ejecutaremos:
pnpm astro add tailwind
Respondemos Y (sí) para instalar automáticamente todas las dependencias necesarias y
también para generar el archivo de configuración de tailwind (tailwind.css), es decir dos
veces. Con esto, ya podemos comenzar a utilizar tailwind y a dar estilos con él.
Antes de comenzar a cambiar los estilos del componente, se debe importar el archivo global.css en el
frontmatter de la página que utilizará el mismo ( en este caso index.astro), como se
muestra a continuación:
---
import '../styles/global.css';
import Post from "../components/Post.astro"
---
Para darle estilos al componente Post, utiliza los estilos del ejemplo o los que gustes para el
componente.
---
---
<article id="article"
class="flex max-w-sm flex-col gap-2 rounded bg-gray-800 px-5 py-6 text-white">
<img class="rounded w-full h-48 object-cover" src="/img/gato.webp" alt="gatito">
<h2 class="text-2xl font-bold">The Post Title</h2>
<p>A post description</p>
<p class="text-zinc-300">
Lorem ipsum dolor sit, amet consectetur adipisicing elit.
Neque eius distinctio aspernatur incidunt,
tempore expedita architecto est,
sint natus, nihil quaerat nisi.
Saepe pariatur nihil fugit itaque
esse doloribus corporis!
</p>
<p class="text-zinc-300">10/12/2023 - 16 min read</p>
</article>
Usar un componente de forma repetida
Si se quisiera mostrar cuatro post en una página (index.astro), por ejemplo, se puede ir añadiendo
la etiqueta con el nombre del componente <Post/> cuatro veces.
Evidentemente todos los cuatro post que se muestran tienen el mismo contenido ya que en estos momentos, el componente es estático. Los componentes estáticos (donde básicamente el contenido no cambia) son excelentes para crear una estructura y diseño básico: encabezados, pies de página, tarjeta del autor, navegación y en general, elementos de la interfaz que no requieren actualizaciones. Ejemplo:
---
import '../styles/global.css';
import Post from "../components/Post.astro"
---
<html lang="es-Es">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body class="container w-5/6 p-4">
<h1>Usar componente</h1>
<Post/>
<Post/>
<Post/>
<Post/>
</body>
</html>
En el navegador, se verían las cuatro tarjetas o posts seguidos:
Hacer un componente reutilizable
Para hacer un componente reutilizable se utilizan los props.
Los props son una manera de pasarle datos a un componente para hacerlo reutilizable. Equivaldrían
más o menos a los argumentos de una función. Para pasarle información o props a un componente, se
pueden usar tres maneras.
-
Enviarle texto al componente:
Desde el archivo donde se incluye el componente se pasan los
propsde manera similar a los argumentos de una función. Ejemplo:<Post title="Nuevo post" description="Descripción del nuevo post."/>Y en el componente
Post.astro, se leería de la siguiente manera.En el frontmatter se escribiría lo siguiente:
--- const {title, description } = Astro.props ---Y en el html de
Post.astro:<article id="article" class="flex max-w-sm flex-col gap-2 rounded bg-gray-800 px-5 py-6 text-white"> <img class="rounded w-full h-48 object-cover" src="/img/gato.webp" alt="gatito"> <h2 class="text-2xl font-bold">{title}</h2> <p>{description}</p> <p class="text-zinc-300"> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Neque eius distinctio aspernatur incidunt, tempore expedita architecto est, sint natus, nihil quaerat nisi. Saepe pariatur nihil fugit itaque esse doloribus corporis! </p> <p class="text-zinc-300">10/12/2023 - 16 min read</p> </article> -
Enviar un objeto de JavaScript con toda la información
Las primeras llaves (exteriores) indican que el contenido será código JavaScript, no un texto. Las llaves interiores indican que el código es un objeto. Desde el archivo donde se incluye el componente:
<Post post = {{ title: ‘Un nuevo post’, description: ‘Descripción del nuevo post’ }}/>Y en el componente
Post.astro, se leería de la siguiente manera:En el
frontmatterse escribiría lo siguiente:--- const {post} = Astro.props ---Y en el html de
Post.astro,<article id="article" class="flex max-w-sm flex-col gap-2 rounded bg-gray-800 px-5 py-6 text-white"> <img class="rounded w-full h-48 object-cover" src="/img/gato.webp" alt="gatito"> <h2 class="text-2xl font-bold">{post.title}</h2> <p>{post.description}</p> <p class="text-zinc-300"> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Neque eius distinctio aspernatur incidunt, tempore expedita architecto est, sint natus, nihil quaerat nisi. Saepe pariatur nihil fugit itaque esse doloribus corporis! </p> <p class="text-zinc-300">10/12/2023 - 16 min read</p> </article>O aplicando destructuración
--- const {post: {title, description}} = Astro.props ---<article id="article" class="flex max-w-sm flex-col gap-2 rounded bg-gray-800 px-5 py-6 text-white"> <img class="rounded w-full h-48 object-cover" src="/img/gato.webp" alt="gatito"> <h2 class="text-2xl font-bold">{title}</h2> <p>{description}</p> ... -
Pasar la información desde una Base de Datos
Uno de los métodos más usuales, es que la información de los post no se escriba de forma estática en el código sino que se tengan los post guardados en una base de datos. Para simular que tenemos una base de datos.
Para este ejemplo, utilizaremos una librería llamada
JSON-Serverla cual es muy útil para pruebas porque simula que tenemos una base de datos. Lo haremos todo, en el próximo punto.
Instalar la librería JSON-Server
Para instalar la librería, detenemos el servidor (Ctrl C) y ejecutamos desde la terminal:
pnpm install -g json-server
La dirección de esta librería es npmjs.com/package/json-server
Una vez instalada, creamos un archivo con un código json y ponemos las distintas propiedades. Esto
va a simular que tenemos una base de datos a la cual nos va a dejar hacer peticiones, crear recursos y demás. Lo
hacemos en la raíz del proyecto creando un archivo llamado db.json.
En este archivo, pegamos todos los datos que queremos simular que tenemos dentro de una base de datos. Ejemplo:
Para ejecutar la BBDD usamos el siguiente comando desde la terminal:
json-server --watch db.json
En este caso el nombre de nuestro archivo JSON es db.json.
Se nos dará una URL accesible (Endpoint) https://localhost:3000/posts, desde la cual podemos acceder a los datos.
Si la abrimos en el navegador, podemos apreciar que tenemos el JSON con la información de todos los posts.
Traer datos de JSON
Para traer los datos y mostrarlos en los post abrimos el archivo donde se incluye el componente. Usaremos el
frontmatter de nuestro archivo ya que la petición se va a ejecutar del lado del servidor.
Desde el frontmatter, podemos hacer peticiones a nuestra base de datos y utilizar datos que no
queremos que se expongan en el navegador del cliente.
Hacemos un fetch para traernos datos de la red:
---
import '../styles/global.css';
import Post from "../components/Post.astro";
const respuesta = await fetch("http://localhost:3000/posts");
const post = await respuesta.json();
---
Una de las grandes ventajas de Astro es que tiene una funcionalidad que se conoce como
top level await y que básicamente significa que se puede utilizar el await fuera de una
función asíncrona. Con lo cual para extraer los post, simplemente utilizamos las dos lineas que están debajo del
import.
Como desde el HTML del archivo podemos acceder ahora a cualquier variable que se haya definido en el
frontmatter, quitaremos los cuatro componentes <Post/> que habíamos creado de
forma estática y mostraremos un post por cada post que tengamos en la base de datos.
Para ello, convertiremos el array de objetos en un array de componentes post utilizando el método
.map. Para indicar al HTML en Astro que insertaremos Javascript, utilizamos
llaves { }. Accedemos a la variable post y hacemos un map para
renderizar por cada uno de los post, un componente <Post/>.
<body class="container w-5/6 p-4 mx-auto">
<h1class="text-3xl text-center">Leer desde BBDD</h1>
<!-- div para mostrar los posts en forma de galería -->
<div class="grid max-w-7xl mx-auto w-full justify-center
justify-items-center items-center gap-4 px-2 sm:grid-cols-2
lg:grid-cols-3">
{ post.map((post: any) => <Post post={post}/>) }
</div>
</body>
El map de Javascript recibe cada uno de los post y por cada post devuelve un componente
<Post/> y a ese componente, le pasamos un prop llamado post con
el valor de cada uno de los post del array.
En el componente Post.astro, se leería de la siguiente manera:
Frontmatter:
---
const {post: {title, description, content, created_at, image}} = Astro.props
---
HTML:
<article class="flex max-w-sm flex-col gap-2 rounded bg-gray-800
px-5 py-6 text-white">
<img class="rounded w-full h-48 object-cover" src={image} alt={title}>
<h2 class="text-2xl font-bold">{title}</h2>
<p>{description}</p>
<p class="text-zinc-300">
{content.slice(0,100) + "..."}
</p>
<p class="text-zinc-300">{new Date(created_at).toLocaleDateString()}</p>
</article>
Ahora, abrimos una nueva terminal, con el botón +

Ejecutamos nuevamente el comando para el servidor de desarrollo
pnpm run dev
Es IMPORTANTE que queden dos procesos corriendo al mismo tiempo, cada uno en su propia pestaña:
Pestaña 1:Servidor de Base de Datos. Ejecutando la "base de datos" con
json-server --watch db.json
Pestaña 2: Servidor de Aplicación. Ejecutando el proyecto de Astro con
pnpm run dev.
Y si vamos al navegador, podremos ver los distintos posts de la Base de Datos.