Tabla de Contenidos
¡Esta es una revisión vieja del documento!
9. Angular: Material
En este tema vamos a crear un proyecto de Angular y usar en sus páginas los componentes de Angular Material: botones, cards, tabs y tablas.
Crear una aplicación de Angular
- Instala Angular CLI v22
npm install @angular/cli@22 -g
ng no arranca y solo dice The Angular CLI requires a minimum Node.js version. Comprueba la versión con node -v.
- Crea una aplicación de Angular
ng new prueba
Las preguntas que hacen se responden de la siguiente forma:
Which stylesheet system would you like to use?: Sass (SCSS)Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)?: NWhich AI tools should Angular integrate with?: None
Establecer el idioma y moneda
Una cosa importante que siempre hay que hacer en un proyecto de Angular es definir que el idioma y la moneda es 'es-ES' y 'EUR' para ello modificaremos el fichero app.config.ts
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import localeEs from '@angular/common/locales/es';
import { registerLocaleData } from '@angular/common';
import { LOCALE_ID,DEFAULT_CURRENCY_CODE } from '@angular/core';
registerLocaleData(localeEs);
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(routes),
{provide: LOCALE_ID, useValue: 'es-ES' },
{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' }
]
};
Ejecutar la App
Modificar la página de inicio
- Modifica el fichero
app.htmlde forma que quede así:
<style> </style> <h1 >Hola mundo</h1> <button > Aceptar </button> <router-outlet />
Los dos ficheros importantes son:
app.ts: Es el fichero TypeScript que contiene la lógica del componente. Es la parte que hay que programar.app.html: Es el fichero HTML que contiene como se muestra ese componente.
- Vamos a dejar el componente de
appde la siguiente forma
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
imports: [RouterOutlet],
templateUrl: './app.html',
styleUrl: './app.scss'
})
export class App {
protected readonly title = signal('prueba');
}
<style> </style> <h1 >Hola mundo</h1> <button > Aceptar </button> <router-outlet />
Añadir CSS en Angular
Para añadir estilos CSS en Angular se usa el fichero de SASS que está en src/styles.scss donde se indican los estilos globales de la App. Aunque también cada componente tiene sus propios estilos.
Por otro lado si queremos añadir otros ficheros CSS o SASS deberemos añadirlos en el JSON de angular.json del raíz. En el nodo /projects/EjemploComponentesAngular/architect/build/options/styles Siendo EjemploComponentesAngular el nombre del proyecto.
Lo mismo se aplica a JavaScript que se añade en /projects/EjemploComponentesAngular/architect/build/options/scripts
Usar Bootstrap en Angular
Por ejemplo para usar Bootstrap junto a su JavaScript se haría de la siguiente forma:
- Descarga bootstrap. Para ello ver la última versión en
https://www.npmjs.com/package/bootstrap?activeTab=versions
npm install bootstrap@v5.3.8
Eso descargará bootstrap en node_modules/bootstrap/
Y ahora añadir el CSS y JavaScript en angular.json:
"build": {
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles.scss"
],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
]
}
Usar Tailwind CSS en Angular
Para usar Tailwind CSS en Angular se añade con:
ng add tailwindcss
Eso hace todo lo necesario:
- Instala
tailwindcss,@tailwindcss/postcssypostcss. - Crea
.postcssrc.jsoncon el plugin de Tailwind para PostCSS. - Crea
src/tailwind.csscon@import 'tailwindcss';y lo añade astylesdeangular.json, delante desrc/styles.scss.
styles.scss: Tailwind 4 no está pensado para usarse con Sass, por eso va en su propio fichero .css.
Añadir Angular Material
La librería que vamos a usar en este módulo es Angular Material
- Para añadirla al proyecto se usa:
ng add @angular/material
Las preguntas que hacen se responden de la siguiente forma:
Select a pair of starter prebuilt color palettes for your Angular Material theme: Azure/Blue
Usar Angular Material
Para usar un componente,primero debes importarlo en TypeScript.
Lo primero es buscar el componente en https://material.angular.dev/components/categories y una vez en el componente copiar el import que hay en la pestaña API.
Veamos el ejemplo con el componente Button.
- Navega a Button y pincha en la pestaña de
API, copia lo siguiente:
import {MatButtonModule} from '@angular/material/button';
- Ve al fichero
app.tsy pega lo que acabas de copiar.
- En el array de
importsañade lo que has importado; que esMatButtonModule:
imports: [RouterOutlet,MatButtonModule],
El fichero app.ts debe quedar así:
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {MatButtonModule} from '@angular/material/button';
@Component({
selector: 'app-root',
imports: [RouterOutlet, MatButtonModule],
templateUrl: './app.html',
styleUrl: './app.scss'
})
export class App {
protected readonly title = signal('EjemploComponentesAngular');
}
- Ahora ya puedes transformar un botón "normal" en un botón de Angular Material añadiendo el atributo
matButton
<style> </style> <h1 >Hola mundo</h1> <button matButton> Aceptar </button> <router-outlet />
Botones
El atributo matButton admite un valor para elegir el estilo del botón.
- Cambia para que sea
matButton="filled"y verás como cambia el estilo del botón.
<style> </style> <h1 >Hola mundo</h1> <button matButton="filled"> Aceptar </button> <router-outlet />
Los estilos que hay son:
matButton: Botón de solo texto.matButton="filled": Botón relleno con el color principal.matButton="tonal": Botón relleno con un color más suave.matButton="outlined": Botón con borde y sin relleno.matButton="elevated": Botón con sombra.
<button matButton="filled">Pagar carrito</button> <button matButton="outlined">Seguir comprando</button> <button matButton>Cancelar</button>
Vuelve a los ejemplos que hay en Button y prueba otros estilos de botones
Iconos
Prueba ahora a añadir un icono de Angular Material; para ello ve a https://material.angular.dev/components/icon/overview
Y fíjate que ahora ya no se añade un atributo a una etiqueta HTML sino que directamente es la etiqueta mat-icon.
- Importa el módulo y añádelo al array de
importsdeapp.ts, igual queMatButtonModule:
import {MatIconModule} from '@angular/material/icon';
- Y úsalo en el HTML:
<mat-icon>shopping_cart</mat-icon> <button matIconButton> <mat-icon>delete</mat-icon> </button>
matIconButton: Botón que solo tiene un icono.
El nombre del icono es el que aparece en Material Symbols.
Cards
Una card agrupa la información de una cosa: un producto, una noticia, un usuario…
- Importa el módulo y añádelo al array de
importsdeapp.ts, igual queMatButtonModule:
import {MatCardModule} from '@angular/material/card';
- Y úsalo en el HTML:
<mat-card appearance="outlined" style="max-width: 300px">
<mat-card-header>
<mat-card-title>Monitor Acer Nitro 23.8"</mat-card-title>
<mat-card-subtitle>89,99 €</mat-card-subtitle>
</mat-card-header>
<img mat-card-image src="https://logongas.es/lib/exe/fetch.php?cache=&media=clase:daw:diw:monitor.webp">
<mat-card-content>
<p>Monitor FHD de 200Hz con FreeSync Premium y HDR10.</p>
</mat-card-content>
<mat-card-actions>
<button matButton="filled">Comprar</button>
<button matButton>Ver detalles</button>
</mat-card-actions>
</mat-card>
Las partes de la card son:
mat-card-header: La cabecera conmat-card-titleymat-card-subtitle.mat-card-image: Atributo para una imagen que ocupa todo el ancho de la card.mat-card-content: El contenido.mat-card-actions: Los botones de la card.
Más información: Card
Tabs
Las pestañas (tabs) muestran distinto contenido en el mismo sitio, una pestaña cada vez.
- Importa el módulo y añádelo al array de
importsdeapp.ts, igual queMatButtonModule:
import {MatTabsModule} from '@angular/material/tabs';
- Y úsalo en el HTML:
<mat-tab-group>
<mat-tab label="Descripción">
<p>Monitor FHD de 200Hz con FreeSync Premium y HDR10.</p>
</mat-tab>
<mat-tab label="Especificaciones">
<p>23.8 pulgadas, panel VA, 1920x1080.</p>
</mat-tab>
<mat-tab label="Opiniones">
<p>4,6 de 5 en 1472 opiniones.</p>
</mat-tab>
</mat-tab-group>
mat-tab-group: El conjunto de pestañas.mat-tab: Cada pestaña. El texto de la pestaña va enlabely su contenido dentro de la etiqueta.
Más información: Tabs
Tablas
La tabla de Material no se escribe fila a fila: se le pasa un array con los datos y se define cómo se muestra cada columna.
- Importa el módulo y define los datos y las columnas en el
.ts:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {MatTableModule} from '@angular/material/table';
@Component({
selector: 'app-root',
imports: [RouterOutlet, MatTableModule],
templateUrl: './app.html',
styleUrl: './app.scss'
})
export class App {
columnas: string[] = ['nombre', 'precio', 'stock'];
productos = [
{nombre: 'Monitor Acer Nitro 23.8"', precio: 89.99, stock: 12},
{nombre: 'Teclado mecánico', precio: 49.90, stock: 0},
{nombre: 'Ratón inalámbrico', precio: 19.95, stock: 34},
];
}
- En el HTML se define cada columna y luego las filas:
<table mat-table [dataSource]="productos">
<ng-container matColumnDef="nombre">
<th mat-header-cell *matHeaderCellDef>Nombre</th>
<td mat-cell *matCellDef="let producto">{{producto.nombre}}</td>
</ng-container>
<ng-container matColumnDef="precio">
<th mat-header-cell *matHeaderCellDef>Precio</th>
<td mat-cell *matCellDef="let producto">{{producto.precio}}</td>
</ng-container>
<ng-container matColumnDef="stock">
<th mat-header-cell *matHeaderCellDef>Stock</th>
<td mat-cell *matCellDef="let producto">{{producto.stock}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnas"></tr>
<tr mat-row *matRowDef="let fila; columns: columnas;"></tr>
</table>
Las piezas son:
[dataSource]: El array con los datos. Cada elemento es una fila.matColumnDef: Define una columna con un nombre. Dentro van su cabecera (mat-header-cell) y su celda (mat-cell).*matCellDef="let producto":productoes el elemento del array de esa fila.*matHeaderRowDefy*matRowDef: Las columnas que se muestran y en qué orden. Es el arraycolumnasdel.ts.
ng-container, solo el array columnas.
Más información: Table
Límites de tamaño (budgets)
ng serve no comprueba el tamaño de la aplicación, pero ng build sí: compila para producción y comprueba los budgets de angular.json. Si se pasa del límite de error, el build falla y no genera nada. La aplicación que funcionaba con ng serve no se puede desplegar, por ejemplo con el ng build del Dockerfile de Docker en el proyecto.
Cómo se usa ng build está en Automatización del proyecto.
Los límites que trae un proyecto nuevo son:
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
]
initial: Lo que descarga el navegador al abrir la aplicación. Con Angular Material ya se pasa de 500kB, pero es solo un warning y el build termina bien. Es normal y se deja así:
▲ [WARNING] bundle initial exceeded maximum budget. Budget 500.00 kB was not met by 8.78 kB with a total of 508.78 kB.
anyComponentStyle: El CSS de cada componente, ya compilado de SASS. Este es el que rompe: a partir de 8kB es un error.
✘ [ERROR] src/app/features/productos/pages/productos/productos.scss exceeded maximum budget. Budget 8.00 kB was not met by 3.04 kB with a total of 11.04 kB.
mat.theme en el .scss de un componente ya son casi 8kB de CSS, porque genera todos los System Tokens; con dos, el build falla. Los temas van siempre en src/styles.scss, que no tiene límite por componente. No subas el maximumError para quitar el error: mueve el tema a styles.scss.
Ejercicios
Ejercicio 1
Crea una página de una tienda que tenga:
- Una tabla con al menos 5 productos y las columnas nombre, precio y stock.
- Debajo, una card por cada uno de 3 productos destacados con imagen, nombre, precio y los botones "Comprar" (relleno) y "Ver detalles" (solo texto).
- Unas tabs con "Descripción", "Especificaciones" y "Opiniones".
