|
| 1 | +# Layouts |
| 2 | + |
| 3 | +Hasta ahora hemos estado repetiendo headers y footers en cada página, eso no es muy buena idea, ¿Os acordáis del concepto de layout que vimos en React? Pues en Astro tenemos cosas parecidas, vamos a verlas. |
| 4 | + |
| 5 | +# Manos a la obra |
| 6 | + |
| 7 | +Vamos a definir el layout base para nuestro sitio: |
| 8 | + |
| 9 | +_./src/layouts/base.astro_ |
| 10 | + |
| 11 | +```astro |
| 12 | +--- |
| 13 | +import Header from '../components/header.astro'; |
| 14 | +import Footer from '../components/footer.astro'; |
| 15 | +import '../styles/global.css'; |
| 16 | +const pageTitle = "Home Page"; |
| 17 | +--- |
| 18 | +<html lang="en"> |
| 19 | + <head> |
| 20 | + <meta charset="utf-8" /> |
| 21 | + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |
| 22 | + <meta name="viewport" content="width=device-width" /> |
| 23 | + <meta name="generator" content={Astro.generator} /> |
| 24 | + <title>{pageTitle}</title> |
| 25 | + </head> |
| 26 | + <body> |
| 27 | + <Header /> |
| 28 | + <h1>{pageTitle}</h1> |
| 29 | + <Footer /> |
| 30 | + <script> |
| 31 | + import "../scripts/hamburger.ts"; |
| 32 | + </script> |
| 33 | + </body> |
| 34 | +</html> |
| 35 | +``` |
| 36 | + |
| 37 | +Hasta aquí más o menos, pero entre el _header_ y el _footer_ tiene que ir el contenido ¿Qué hacemos? En React tenemos la propiedad _children_ que nos permite hacer esto, en Astro tenemos algo parecido, se llama _slot_. |
| 38 | + |
| 39 | +```diff |
| 40 | + <body> |
| 41 | + <Header /> |
| 42 | + <h1>{pageTitle}</h1> |
| 43 | ++ <slot /> |
| 44 | + <Footer /> |
| 45 | + <script> |
| 46 | + import "../scripts/menu.js"; |
| 47 | + </script> |
| 48 | + </body> |
| 49 | +``` |
| 50 | + |
| 51 | +Vamos ahora a usarlo en la página principal: |
| 52 | + |
| 53 | +_./src/pages/index.astro_ |
| 54 | + |
| 55 | +```diff |
| 56 | +--- |
| 57 | +- import "../styles/global.css"; |
| 58 | +- import Header from "../components/header.astro"; |
| 59 | +- import Footer from "../components/footer.astro"; |
| 60 | ++ import BaseLayout from '../layouts/base.astro'; |
| 61 | +--- |
| 62 | + |
| 63 | +- <html lang="en"> |
| 64 | +- <head> |
| 65 | +- <meta charset="utf-8" /> |
| 66 | +- <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |
| 67 | +- <meta name="viewport" content="width=device-width" /> |
| 68 | +- <meta name="generator" content={Astro.generator} /> |
| 69 | +- <title>Mi Blog de Ejemplo</title> |
| 70 | +- </head> |
| 71 | +- <body> |
| 72 | +- <Header /> |
| 73 | ++ <BaseLayout> |
| 74 | +- <h1>Mi blog de Ejemplo 3</h1> |
| 75 | ++ <p>contenido de la página</p> |
| 76 | ++ </BaseLayout> |
| 77 | +- <Footer /> |
| 78 | +- <script> |
| 79 | +- import "../scripts/hamburger.ts"; |
| 80 | +- </script> |
| 81 | +- </body> |
| 82 | +-</html> |
| 83 | +``` |
| 84 | + |
| 85 | +Nos falta un detalle, para el _h1_ del layout base, lo suyo es poder pasarle el contenido por prop, vamos a ello: |
| 86 | + |
| 87 | +_./src/layouts/base.astro_ |
| 88 | + |
| 89 | +```diff |
| 90 | +--- |
| 91 | +import Header from "../components/header.astro"; |
| 92 | +import Footer from "../components/footer.astro"; |
| 93 | +import "../styles/global.css"; |
| 94 | +- const pageTitle = "Home Page"; |
| 95 | ++ interface Props { |
| 96 | ++ pageTitle: string; |
| 97 | ++ } |
| 98 | ++ |
| 99 | ++ const { pageTitle } = Astro.props as Props; |
| 100 | +--- |
| 101 | + |
| 102 | +<html lang="en"> |
| 103 | +``` |
| 104 | + |
| 105 | +Y ahora en Index: |
| 106 | + |
| 107 | +_./src/pages/index.astro_ |
| 108 | + |
| 109 | +```diff |
| 110 | +--- |
| 111 | +import BaseLayout from "../layouts/base.astro"; |
| 112 | +--- |
| 113 | + |
| 114 | +<BaseLayout |
| 115 | ++ pageTitle="Página principal" |
| 116 | +> |
| 117 | + <p>Contenido de la página</p> |
| 118 | +</BaseLayout> |
| 119 | +``` |
| 120 | + |
| 121 | +Ahora podemos aplicar esto en las otras dos páginas: |
| 122 | + |
| 123 | +_./src/pages/blog.astro_ |
| 124 | + |
| 125 | +```diff |
| 126 | +--- |
| 127 | +- import "../styles/global.css"; |
| 128 | +- import Navigation from "../components/navigation.astro"; |
| 129 | ++ import BaseLayout from "../layouts/base.astro"; |
| 130 | +--- |
| 131 | +``` |
| 132 | + |
| 133 | +```diff |
| 134 | +- <html lang="en"> |
| 135 | +- <head> |
| 136 | +- <meta charset="utf-8" /> |
| 137 | +- <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |
| 138 | +- <meta name="viewport" content="width=device-width" /> |
| 139 | +- <meta name="generator" content={Astro.generator} /> |
| 140 | +- <title>Acerca de</title> |
| 141 | +- </head> |
| 142 | +- <body> |
| 143 | +- <Navigation /> |
| 144 | ++ <BaseLayout pageTitle="Blog"> |
| 145 | +- <h1>Blog</h1> |
| 146 | + <h2>Aqui va mi listado de posts</h2> |
| 147 | + <ul> |
| 148 | + <li><a href="/posts/post-1/">Post 1</a></li> |
| 149 | + <li><a href="/posts/post-2/">Post 2</a></li> |
| 150 | + <li><a href="/posts/post-3/">Post 3</a></li> |
| 151 | + </ul> |
| 152 | ++ <BaseLayout> |
| 153 | + </body> |
| 154 | +</html> |
| 155 | +``` |
| 156 | + |
| 157 | +_./src/pages/about.astro_ |
| 158 | + |
| 159 | +```diff |
| 160 | +--- |
| 161 | +- import "../styles/global.css"; |
| 162 | +import type { Identity } from "./about.model.ts"; |
| 163 | +- import Navigation from "../components/navigation.astro"; |
| 164 | + |
| 165 | +const pageTitle = "Acerca de dinámico"; |
| 166 | + |
| 167 | +const identity: Identity = { |
| 168 | + firstName: "Paquillo", |
| 169 | + country: "Argentina", |
| 170 | + occupation: "Programador", |
| 171 | + hobbies: ["fotografía", "beber cerveza", "futbol"], |
| 172 | +}; |
| 173 | + |
| 174 | +const skills = ["HTML", "CSS", "JavaScript", "React", "Astro"]; |
| 175 | +const happy = true; |
| 176 | +const finished = false; |
| 177 | +const goal = 3; |
| 178 | + |
| 179 | +const skillColor = "navy"; |
| 180 | +--- |
| 181 | +``` |
| 182 | + |
| 183 | +```diff |
| 184 | +- <html lang="en"> |
| 185 | +- <head> |
| 186 | +- <meta charset="utf-8" /> |
| 187 | +- <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |
| 188 | +- <meta name="viewport" content="width=device-width" /> |
| 189 | +- <meta name="generator" content={Astro.generator} /> |
| 190 | +- <title>{pageTitle}</title> |
| 191 | +- <style define:vars={{ skillColor }}> |
| 192 | +- h1 { |
| 193 | +- color: purple; |
| 194 | +- font-size: 4rem; |
| 195 | +- } |
| 196 | +- .skill { |
| 197 | +- color: var(--skillColor); |
| 198 | +- font-weight: bold; |
| 199 | +- } |
| 200 | +- </style> |
| 201 | +- </head> |
| 202 | +- <body> |
| 203 | +- <Navigation /> |
| 204 | +- |
| 205 | +- <h1>{pageTitle}</h1> |
| 206 | ++ <BaseLayout pageTitle={pageTitle}> |
| 207 | + <h2>Y mi nuevo blog</h2> |
| 208 | + |
| 209 | + <p>Esta es la página de "acerca de".</p> |
| 210 | + |
| 211 | + <ul> |
| 212 | + <li>Me llamo {identity.firstName}.</li> |
| 213 | + <li>vivo en {identity.country} y trabajo com {identity.occupation}.</li> |
| 214 | + { |
| 215 | + identity.hobbies.length >= 2 && ( |
| 216 | + <li> |
| 217 | + Dos de mis hobbies son: {identity.hobbies[0]} and{" "} |
| 218 | + {identity.hobbies[1]} |
| 219 | + </li> |
| 220 | + ) |
| 221 | + } |
| 222 | + </ul> |
| 223 | + <p>Mis skills</p> |
| 224 | + <ul> |
| 225 | + {skills.map((skill) => <li class="skill">{skill}</li>)} |
| 226 | + </ul> |
| 227 | + {happy && <p>Estoy mu contento !</p>} |
| 228 | + |
| 229 | + {finished && <p>He completado este tutorial</p>} |
| 230 | + |
| 231 | + { |
| 232 | + goal === 3 ? ( |
| 233 | + <p>Mi meta es completarlo en 3 días.</p> |
| 234 | + ) : ( |
| 235 | + <p>Pues no llevo tres días.</p> |
| 236 | + ) |
| 237 | + } |
| 238 | ++ </BaseLayout> |
| 239 | +``` |
0 commit comments