Saltar a contenido

4. Componentes: la unidad fundamental

Qué son, para qué sirven y cómo se componen.

Generar un componente

ng g c students/student-card

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

saludo.component.ts
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';
}
saludo.component.html
<p>Hola, {{ nombre }} 👋</p>
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:

app.component.ts
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
students/student-card/student-card.component.ts
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);
  }
}
students/student-card/student-card.component.html
<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.