4. Componentes: la unidad fundamental¶
Qué son, para qué sirven y cómo se componen.
Generar un componente¶
Genera cuatro archivos:
| Archivo | Contenido |
|---|---|
student-card.component.ts |
Clase, estado y acciones |
student-card.component.html |
Template |
student-card.component.css |
Estilos encapsulados |
student-card.component.spec.ts |
Pruebas |
Anatomía¶
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-saludo',
standalone: true,
templateUrl: './saludo.component.html',
styleUrl: './saludo.component.css'
})
export class SaludoComponent {
@Input() nombre = 'estudiante';
}
| Metadato | Significado |
|---|---|
selector |
Etiqueta HTML con la que se usa el componente (<app-saludo>) |
standalone: true |
El componente declara por sí mismo lo que necesita (imports) |
templateUrl |
Archivo con el HTML |
styleUrl |
Archivo con el CSS; los estilos solo afectan a este componente |
-
¿Qué es?
Una pieza autónoma de interfaz que agrupa estructura HTML, comportamiento TypeScript y estilos.
-
¿Para qué sirve?
Permite dividir una pantalla grande en piezas pequeñas, reutilizables, comprensibles y fáciles de probar.
-
Cómo se relacionan sus archivos
.ts: clase, estado y acciones ·.html: template ·.css: estilos encapsulados ·.spec.ts: pruebas. -
Comunicación
Recibe datos del padre mediante entradas y comunica eventos mediante salidas. Delega la lógica compartida a servicios.
Usar un componente dentro de otro¶
El componente padre lo importa y usa su selector:
import { Component } from '@angular/core';
import { SaludoComponent } from './saludo/saludo.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [SaludoComponent],
template: `
<app-saludo /> <!-- Hola, estudiante -->
<app-saludo nombre="Ana" /> <!-- Hola, Ana -->
`
})
export class AppComponent {}
La aplicación completa es un árbol de componentes que empieza en AppComponent:
flowchart TD
A[AppComponent] --> L[StudentListComponent]
L --> C1[StudentCardComponent]
L --> C2[StudentCardComponent]
L --> C3[StudentCardComponent]
Del template a la clase: data binding¶
| Sintaxis | Dirección | Ejemplo |
|---|---|---|
{{ expr }} |
Clase → vista (texto) | {{ student.name }} |
[propiedad]="expr" |
Clase → vista (propiedad) | [disabled]="saving" |
(evento)="metodo()" |
Vista → clase | (click)="select()" |
<h3>{{ student.name }}</h3>
<span [class.inactive]="!student.active">{{ student.email }}</span>
<button (click)="select()">Ver detalle</button>
Control de flujo¶
@if (students.length === 0) {
<p>No hay estudiantes registrados.</p>
}
@for (s of students; track s.id) {
<app-student-card [student]="s" />
}
track s.id le indica a Angular cómo identificar cada elemento para no redibujar toda la lista cuando cambia uno.
Comunicación: entradas y salidas¶
Los datos bajan, los eventos suben.
flowchart TD
P[Padre] -- "@Input: datos" --> H[Hijo]
H -- "@Output: eventos" --> P
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Student } from '../../models/student.model';
@Component({
selector: 'app-student-card',
standalone: true,
templateUrl: './student-card.component.html',
styleUrl: './student-card.component.css'
})
export class StudentCardComponent {
@Input({ required: true }) student!: Student;
@Output() selected = new EventEmitter<number>();
select(): void {
this.selected.emit(this.student.id);
}
}
<article>
<h3>{{ student.name }}</h3>
<p>{{ student.email }}</p>
<button (click)="select()">Ver detalle</button>
</article>
El padre entrega el dato con [student] y escucha el evento con (selected); $event contiene lo emitido:
@for (s of students; track s.id) {
<app-student-card [student]="s" (selected)="goToDetail($event)" />
}
Sintaxis con signals
Angular también ofrece input() y output() como funciones (student = input.required<Student>()). Son equivalentes; la presentación usa los decoradores @Input / @Output.
Idea clave
Un componente debe resolver una responsabilidad visual concreta. Si intenta hacerlo todo, deja de ser reutilizable y mantenible.
Siguiente: Modelos.