Arquitectura de layouts
Cuando se inicia un proyecto de Angular siempre surge la pregunta sobre qué estructura de carpetas debe tener. No es fácil la respuesta dado que depende de muchos factores como por ejemplo si es solamente una PoC, si se crearán librerías, si participan varios desarrolladores/equipos, y un largo etcétera.
Quizás empiece siendo un proyecto generado automáticamente mediante Angular CLI y que a lo largo de su vida irá transformándose según las necesidades pero lo ideal sería empezar con una buena base y que sea escalable.
En este artículo, vamos a centrarnos bajo el punto de vista de la interfaz de usuario (UI) y más concretamente en la arquitectura de layouts o estructura de las «vistas» (el HTML). Imagínatelo como si fuera un diseño wireframe lleno de «cajitas» vacías donde poner componentes.
Problemática
Cuando en un proyecto intervienen varios desarrolladores/equipos, cada persona programará según su aprendizaje/experiencia pero obligatoriamente deberá seguir alguna línea de estilos o un “Design System”.
Aún siguiendo dicha guía, la estructura del HTML puede crearse de diferentes maneras como por ejemplo dónde mostrar los datos en una modal (título, cuerpo y pie) ó en un artículo (título y cuerpo) e incluso en la propia estructura de la página de inicio (cabecera, navegación, artículo, pie, …).
No se desea restringir la creatividad de un desarrollador, simplemente se pretende posicionar de forma correcta las diferentes partes o secciones de la vista de un componente en un layout específico.
Solución
Para solucionar esa problemática nos basaremos en la proyección de contenidos con placeholders. Es decir, crearemos un tipo de componente específico para cada layout siendo de tipo presentacionales sin estado.
De esta manera tenemos algunas ventajas como:
- Ayuda a estructurar las secciones: página inicial, artículos, modales, …
- Homogeneización de las secciones.
- Cambio rápido visual cambiando el layout (HTML y/o estilos CSS)
La forma de proceder sería la siguiente:
- Se establecen unos layouts para cada situación: Principal/Inicio de la aplicación, artículo, modal, …
- Los desarrolladores/equipos crean su componente indicando qué layout necesita y a qué parte del layout tiene que ir esa parte de código (placeholder).
Librería de layouts
Vamos a crear una librería de layouts (@af/layouts) en vez de crear una carpeta «layouts» dentro de la aplicación. De esta manera la librería siempre se puede reutilizar en otros proyectos.
Layout principal
Creamos un layout principal (main) que dispondrá de los elementos de la siguiente manera:
Resultando el código del componente de la siguiente manera:
HTML del componente (main.component.html):
<div class="wrapper">
<header><ng-content select="[header]">Cabecera</ng-content></header>
<article><ng-content select="[article]">Artículo</ng-content></article>
<nav><ng-content select="[nav]">Navegación</ng-content></nav>
<footer><ng-content select="[footer]">Pie</ng-content></footer>
</div>
TS del componente (main.component.ts):
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'af-main-layout',
standalone: true,
imports: [CommonModule],
templateUrl: './main.component.html',
styleUrl: './main.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MainLayoutComponent {}
Estilos del componente (main.component.scss):
.wrapper {
display: grid;
grid-template-areas:
"header header header"
"nav article article"
"footer footer footer";
grid-template-rows: 80px 1fr 50px;
grid-template-columns: 10% 2fr;
gap: 2px;
height: 100vh;
margin: 0;
}
header,
footer,
article,
nav {
padding: 1em;
background: #bae0e2;
}
header {
grid-area: header;
}
footer {
grid-area: footer;
}
article {
grid-area: article;
}
nav {
grid-area: nav;
}
/* Stack the layout on small devices/viewports. */
@media all and (max-width: 575px) {
.wrapper {
grid-template-areas:
"header"
"nav"
"article"
"footer";
grid-template-rows: 80px 50px 1fr 50px;
grid-template-columns: 1fr;
}
}
De esta manera si se desea disponer de diferente manera los elementos, basta o bien cambiando la estructura HTML o cambiar los estilos CSS del layout.
Así para la página inicial dentro de la aplicación, solo hay que usar el «main-layout» y posicionar los diferentes componentes.
Nota: como aquí el selector se hace mediante atributo select="[header]"
, pero se puede indicar mediante el tag, identificadores, o nombres de clases CSS.
Así, en el componente root (app.component.html) de la aplicación, utilizamos el «main-layout», diciéndole qué componente va a qué sección:
<af-main-layout>
<af-header header></af-header>
<af-navigation nav></af-navigation>
<af-footer footer></af-footer>
<af-articles article></af-articles>
</af-main-layout>
Layout article
Otro ejemplo sería el de layout de un artículo, que podemos definir su layout como:
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'af-article-layout',
standalone: true,
imports: [CommonModule],
template: `
<h2><ng-content select="[title-section]">article title</ng-content></h2>
<div>
<ng-content select="[body-section]">body title</ng-content>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ArticleLayoutComponent {}
Y para crear artículos con este layout debería ser:
<af-article-layout>
<ng-content title-section>Artículo 1</ng-content>
<ng-content body-section>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
</ng-content>
</af-article-layout>
Si el día de mañana, por ejemplo, en vez de que el título de los artículos sea un h2, sea un h3, solo hay que modificar el layout y no los artículos.
Resumen
Hemos visto como dividir un layout en secciones usando la proyección de contenidos de Angular para que cada desarrollador coloque parte de su vista en la sección apropiada. De esta manera evitamos que puedan existir diferentes estructuras dado un mismo concepto. Si asignáramos un responsable de los layouts, éste recaería sobre el diseñador que sería la persona encargada de crear nuevos layouts, mover de sitio las secciones, eliminarlas, añadir nuevas, etc.
¿Qué te ha parecido esta división de las vistas? Puedes ver el ejemplo en el repositorio de github.