Angular 21 de Cero a Experto / Fundamentos
input() y output(): datos que bajan, eventos que suben
Un componente aislado no sirve de mucho: la magia está en que las piezas se comuniquen. En
React esto era "props que bajan, callbacks que suben"; en Angular es exactamente el mismo
patrón con otra sintaxis: input() para recibir datos del padre y output() para avisarle
de eventos. Y aquí, por primera vez, aparecen las señales —el sistema reactivo que domina
Angular 21—. Hoy conectas componentes al estilo moderno y das el primer paso hacia signals.
El mismo patrón que ya conoces
input(): recibir datos del padre
En Angular 21, un input se declara llamando a la función input(). Devuelve una SEÑAL: una
propiedad que lees llamándola como función, this.valor():
import { Component, input } from '@angular/core';
@Component({
selector: 'app-tarjeta-gasto',
template: `
<article>
<h3>{{ nombre() }}</h3> <!-- se lee llamándola: nombre() -->
<span>{{ valor() }} COP</span>
</article>
`,
})
export class TarjetaGastoComponent {
nombre = input.required<string>(); // obligatorio: el padre DEBE pasarlo
valor = input(0); // opcional con valor por defecto 0
}El padre le pasa los datos con property binding —los corchetes que ya conoces—:
@Component({
imports: [TarjetaGastoComponent],
template: `
<app-tarjeta-gasto [nombre]="'Café'" [valor]="4500" />
<app-tarjeta-gasto [nombre]="'Almuerzo'" [valor]="18000" />
`,
})
export class ListaComponent {}Es idéntico conceptualmente a las props de React: <TarjetaGasto nombre="Café" valor={4500} />.
La diferencia clave es que en Angular 21 un input es una SEÑAL de solo lectura: se lee con
(), y cuando el padre cambia el valor, todo lo que dependa de esa señal se actualiza solo.
Eso es lo que verás a fondo en el próximo módulo.
output(): avisar al padre
Para que el hijo notifique un evento al padre, se usa output(), que devuelve un emisor con
un método emit():
import { Component, input, output } from '@angular/core';
@Component({
selector: 'app-tarjeta-gasto',
template: `
<article>
<h3>{{ nombre() }}</h3>
<button (click)="borrar.emit(id())">Eliminar</button>
</article>
`,
})
export class TarjetaGastoComponent {
id = input.required<number>();
nombre = input.required<string>();
borrar = output<number>(); // emitirá un número (el id a borrar)
}Y el padre escucha ese evento con event binding —los paréntesis— y recibe el dato en
$event:
@Component({
imports: [TarjetaGastoComponent],
template: `
@for (g of gastos; track g.id) {
<app-tarjeta-gasto
[id]="g.id"
[nombre]="g.nombre"
(borrar)="eliminarGasto($event)" />
}
`,
})
export class ListaComponent {
gastos = [{ id: 1, nombre: 'Café' }, { id: 2, nombre: 'Almuerzo' }];
eliminarGasto(id: number) {
this.gastos = this.gastos.filter((g) => g.id !== id);
}
}Fíjate en el paralelo perfecto con React: allá el hijo recibía una prop onBorrar y la
llamaba (onBorrar(id)); aquí declara un output() y hace borrar.emit(id). El padre,
que en React pasaba la función, en Angular escucha con (borrar)="...". Mismo flujo,
misma dirección: datos abajo, eventos arriba.
model(): two-way binding en un componente
Cuando quieres que un dato viaje en AMBAS direcciones entre padre e hijo (el hijo puede
leerlo y cambiarlo, y el padre se entera), usas model(). Es un input y un output
combinados —la "banana en caja" a nivel de componente—:
import { Component, model } from '@angular/core';
@Component({
selector: 'app-campo-monto',
template: `<input type="number" [value]="monto()" (input)="onInput($event)" />`,
})
export class CampoMontoComponent {
monto = model(0); // two-way: el padre usa [(monto)]="..."
onInput(e: Event) {
this.monto.set(Number((e.target as HTMLInputElement).value));
}
}
// El padre lo usa así: <app-campo-monto [(monto)]="totalGasto" />Antes (@Input/@Output decoradores) vs Ahora (input()/output())
En Angular 21, un input declarado con `input()` es…
Mini-reto
Diseña un TarjetaGastoComponent: 1) recibe nombre (obligatorio) y valor con input();
2) muestra ambos en el template leyéndolos con (); 3) tiene un botón que emite un
output() llamado editar con el id del gasto. Luego escribe cómo lo usaría el padre
dentro de un @for, pasando los inputs con [ ] y escuchando el output con ( ).
Qué sigue
Cerraste el módulo de fundamentos: sabes crear componentes, enlazar datos, decidir, repetir
y comunicar piezas. Y viste asomar las señales en input(). El próximo módulo es el
corazón de Angular 21 —signals a fondo—: qué son, cómo se derivan con computed, cómo
reaccionan con effect, y por qué reemplazaron a zone.js. Antes, un reto para afianzar
todo lo de este módulo.