Skip to content

Commit cf19116

Browse files
committed
readme 15 completed
1 parent 4110295 commit cf19116

1 file changed

Lines changed: 318 additions & 0 deletions

File tree

  • 04-frameworks/15-astro/15-rutas-dinamicas
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
Hemos generado una lista de posts estática, pero ¿qué pasa si queremos mostrar una lista paginada de posts? ¿cómo podría hacerlo?
2+
3+
¿Y si quisiera hacerlo paginado? Aquí podría crearme una pagina con los 10 primeros posts y después generar paginas dinámicas, del tipo `blog/[page].astro` para ir renderizando los posts correspondientes a cada página.
4+
5+
Para ver como funcional las páginas dinámica, vamos a ver un ejemplo más sencillo con los tags de un blog.
6+
7+
¿Qué vamos a hacer?
8+
9+
- Suponemos que tenemos un listado conocido de tags (Esto podría sacarse de una API REST, o de un JSON).
10+
11+
- Vamos a crear un componente que muestra el listado de esos tags, y que al pinchar navege a un tag en concreto (lo utilizaremos en las páginas).
12+
13+
- Cuando el usuario pinche en un tag, le llevamos a una página que tendrá el nombre del tag (será una página dinámica, habrán tantas como tags tenga el array).
14+
15+
- Ahí filtramos de la lista de posts, en el frontmatter los que tengan el tag en concreto y lo mostramos (esto en un proyecto real se podría sacar de una API Rest).
16+
17+
Nos arrancamos por crear la página dinámica.
18+
19+
¿De que va esto?
20+
21+
- Vamos a crear una carpeta `tags` dentro de `pages`
22+
23+
- El nombre del parametro va a tener entre corchete el nombre `[tag]`, con eso le indicamos que ese campo va a ser dinamico.
24+
25+
- Lo siguiente es decirle que valores puede aceptar `[tag]`, con eso Astro ya sabe cuantas páginas estaticas tiene que generar y que plantilla usar (una por cada tag en el array).
26+
27+
- Y ahora nos centrams en el parametro, leemos lo que viene del parametro `tag` (es decir `[tag]`) e indicamos que estams mostrando los posts que tengan ese tag.
28+
29+
_./src/pages/tags/[tag].astro_
30+
31+
```astro
32+
---
33+
import BaseLayout from '../../layouts/base.astro';
34+
35+
// Define los tipos para las rutas estáticas
36+
interface StaticPath {
37+
params: {
38+
tag: string;
39+
};
40+
}
41+
42+
export async function getStaticPaths(): Promise<StaticPath[]> {
43+
return [
44+
{ params: { tag: "astro" } },
45+
{ params: { tag: "blogging" } },
46+
{ params: { tag: "hola mundo" } },
47+
{ params: { tag: "learning in public" } },
48+
{ params: { tag: "successes" } },
49+
];
50+
}
51+
52+
// Tipar los parámetros que recibe la página
53+
interface Params {
54+
tag: string;
55+
}
56+
57+
const { tag } = Astro.params as Params;
58+
---
59+
<BaseLayout pageTitle={tag}>
60+
<p>Posts tagged with {tag}</p>
61+
</BaseLayout>
62+
```
63+
64+
Si ahora visitamos, por ejemplo:
65+
66+
- `http://localhost:3000/tags/astro`
67+
- `http://localhost:3000/tags/blogging`
68+
- `http://localhost:4321/tags/hola%20mundo`
69+
- `http://localhost:3000/tags/learning%20in%20public`
70+
- `http://localhost:3000/tags/successes`
71+
72+
Podemos ver la página correspondiente a cada tag.
73+
74+
Bueno, esto funciona, pero puede ser un poco rollo tener que ir añadiendo tags a mano cada vez que metas uno en un post, ya que esto se ejecuta una sóla vez y ya se genera todo estático, ¿No sería más fácil leer de los posts los tags que tenemos y generar una lista?
75+
76+
Vamos a hacer eso:
77+
78+
- Leemos todos los posts en `.md`.
79+
- Eso `md` tiene definido un frontmatter con los tags.
80+
- Iteramos y leemos las listas.
81+
- Las añadimos a un conjunto de tags.
82+
83+
Para optimizar vamos a leer la lista de posts una sola vez, aprovechando **getStaticPaths**, y s se lo pasamos como prop a cada tag para que despues haga el filtrado.
84+
85+
_./src/pages/tags/[tag].astro_
86+
87+
```diff
88+
---
89+
import BaseLayout from '../../layouts/base.astro';
90+
91+
+ // Deberíamos mover esto a un fichero de modelo común
92+
+ // Ya lo estamos usando en tres sitios
93+
+ interface Frontmatter {
94+
+ layout: string;
95+
+ title: string;
96+
+ pubDate: string;
97+
+ description: string;
98+
+ author: string;
99+
+ image: {
100+
+ url: string;
101+
+ alt: string;
102+
+ };
103+
+ tags: string[];
104+
+ }
105+
+
106+
+ // Tipar las props
107+
+ interface Props {
108+
+ posts: MarkdownInstance<Record<string, Frontmatter>>[];
109+
+ }
110+
111+
// Define los tipos para las rutas estáticas
112+
interface StaticPath {
113+
params: {
114+
tag: string;
115+
+ props: Props;
116+
};
117+
}
118+
119+
export async function getStaticPaths(): Promise<StaticPath[]> {
120+
+ const allPosts = await Astro.glob<Frontmatter>('../posts/*.md');
121+
122+
return [
123+
- { params: { tag: "astro" } },
124+
+ { params: { tag: "astro"}, props: {posts: allPosts} },
125+
- { params: { tag: "blogging" } },
126+
+ { params: { tag: "blogging"}, props: {posts: allPosts} },
127+
- { params: { tag: "hola mundo" } },
128+
+ { params: { tag: "hola mundo"}, props: {posts: allPosts} },
129+
- { params: { tag: "learning in public" } },
130+
+ { params: { tag: "learning in public"}, props: {posts: allPosts} },
131+
- { params: { tag: "successes" }, props: {posts: allPosts} },
132+
];
133+
}
134+
135+
// Tipar los parámetros que recibe la página
136+
interface Params {
137+
tag: string;
138+
}
139+
140+
141+
142+
const { tag } = Astro.params as Params;
143+
+ const { posts } = Astro.props;
144+
+
145+
+ // Ahora sacamos la lista de posts que tengan el tag
146+
+ const filteredPosts = posts.filter(post => post.frontmatter.tags?.includes(tag));
147+
---
148+
```
149+
150+
Y ahora que tenemos esa lista la podemos mostrar en la misma página de tags
151+
152+
_./src/pages/tags/[tag].astro_
153+
154+
```diff
155+
<BaseLayout pageTitle={tag}>
156+
<p>Posts tagged with {tag}</p>
157+
+ <ul>
158+
+ {filteredPosts.map((post) => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
159+
+ </ul>
160+
</BaseLayout>
161+
```
162+
163+
Y ya que estamos, podemos incluso usar el componente Blog Post que creamos antes
164+
165+
- Lo importamos.
166+
- Lo usamos
167+
168+
_./src/pages/tags/[tag].astro_
169+
170+
```diff
171+
---
172+
import type { MarkdownInstance } from "astro";
173+
import BaseLayout from "../../layouts/base.astro";
174+
+ import BlogPost from "../../components/blog-post.astro";
175+
```
176+
177+
```diff
178+
<BaseLayout pageTitle={tag}>
179+
<p>Posts tagged with {tag}</p>
180+
<ul>
181+
{
182+
filteredPosts.map((post) => (
183+
- <li>
184+
- <a href={post.url}>{post.frontmatter.title}</a>
185+
- </li>
186+
+ <BlogPost url={post.url} title={post.frontmatter.title}/>
187+
))
188+
}
189+
</ul>
190+
</BaseLayout>
191+
```
192+
193+
Vamos ahora a crear la página _index_ de las tags que va a tener la colección completa.
194+
195+
Antes que eso vamos a sacar el modelo de _Frontmatter_ a un fichero común.
196+
197+
_./src/models/blog-post-frontmatter.ts_
198+
199+
```typescript
200+
// Deberíamos de llamarlo BlogFrontMatter
201+
export interface Frontmatter {
202+
layout: string;
203+
title: string;
204+
pubDate: string;
205+
description: string;
206+
author: string;
207+
image: {
208+
url: string;
209+
alt: string;
210+
};
211+
tags: string[];
212+
}
213+
```
214+
215+
Y usarlo en `[tag].astro``
216+
217+
_./src/pages/tags/[tag].astro_
218+
219+
```diff
220+
---
221+
import type { MarkdownInstance } from "astro";
222+
import BaseLayout from "../../layouts/base.astro";
223+
+ import type { Frontmatter } from "../../models/blog-post-frontmatter";
224+
225+
- // Deberíamos mover esto a un fichero de modelo común
226+
- // Ya lo estamos usando en tres sitios
227+
- interface Frontmatter {
228+
- layout: string;
229+
- title: string;
230+
- pubDate: string;
231+
- description: string;
232+
- author: string;
233+
- image: {
234+
- url: string;
235+
- alt: string;
236+
- };
237+
- tags: string[];
238+
- }
239+
```
240+
241+
Y ahora vamos a por el index:
242+
243+
_./src/pages/tags/index.astro_
244+
245+
```astro
246+
---
247+
import BaseLayout from "../../layouts/base.astro";
248+
import type { Frontmatter } from "../../models/blog-post-frontmatter";
249+
250+
const allPosts = await Astro.glob<Frontmatter>('../posts/*.md');
251+
// Aquí usamos una aproximación más moderna para sacar los tags
252+
const tags = [...new Set(allPosts.map((post) => post.frontmatter.tags).flat())];
253+
const pageTitle = "Tag Index";
254+
---
255+
```
256+
257+
Y ahora mostramos la lista:
258+
259+
_./src/pages/tags/index.astro_
260+
261+
```diff
262+
---
263+
+ <BaseLayout pageTitle={pageTitle}>
264+
+ <div>{tags.map((tag) => <p>{tag}</p>)}</div>
265+
+ </BaseLayout>
266+
```
267+
268+
Y ya que estamos vamos a convertir esto en enlaces:
269+
270+
_./src/pages/tags/index.astro_
271+
272+
```diff
273+
<BaseLayout pageTitle={pageTitle}>
274+
- <div>{tags.map((tag) => <p>{tag}</p>)}</div>
275+
+ {tags.map((tag) => <p><a href={`/tags/${tag}`}>{tag}</a></p>)}
276+
</BaseLayout>
277+
```
278+
279+
Y le añadimos unos estilos:
280+
281+
```diff
282+
</BaseLayout>
283+
284+
+ <style>
285+
+ a {
286+
+ color: #00539F;
287+
+ }
288+
+
289+
+ .tags {
290+
+ display: flex;
291+
+ flex-wrap: wrap;
292+
+ }
293+
+
294+
+ .tag {
295+
+ margin: 0.25em;
296+
+ border: dotted 1px #a1a1a1;
297+
+ border-radius: .5em;
298+
+ padding: .5em 1em;
299+
+ font-size: 1.15em;
300+
+ background-color: #F8FCFD;
301+
+ }
302+
+ </style>
303+
```
304+
305+
Hey, y si navegamos a `http://localhost:4321/tags`, podemos ver la lista de tags.
306+
307+
Podríamos añadir esta ruta a nuestro menú principal.
308+
309+
_./src/components/navigation.astro_
310+
311+
```diff
312+
<div class="nav-links">
313+
<a href="/">Home</a>
314+
<a href="/about/">About</a>
315+
<a href="/blog/">Blog</a>
316+
+ <a href="/tags/">Tags</a>
317+
</div>
318+
```

0 commit comments

Comments
 (0)