CodeForge

Angular 21 de Cero a Experto / Signals a fondo

Inputs como señales y model(): comunicación reactiva

Teoría22 min20 XP

En el módulo anterior usaste input() para recibir datos del padre. Ahora que dominas las señales, entiendes lo que realmente son: un input ES una señal de solo lectura. Eso desbloquea cosas poderosas —derivar de un input con computed, reaccionar con effect, transformar el valor al entrar— y hace obsoleto el viejo ngOnChanges. Hoy exprimes los signal inputs y model(), el two-way binding reactivo.

Un input es una señal que viene de fuera

input(), input.required() y valores por defecto

tarjeta.component.ts
import { Component, input } from '@angular/core';

@Component({
selector: 'app-tarjeta-gasto',
template: `<h3>{{ nombre() }}</h3><span>{{ valor() }}</span>`,
})
export class TarjetaGastoComponent {
nombre = input.required<string>();   // obligatorio: el compilador exige pasarlo
valor  = input(0);                   // opcional, por defecto 0
moneda = input('COP');               // el tipo se infiere: string
}

input.required<T>() no tiene valor inicial: Angular garantiza en compilación que el padre lo pasa, así que dentro nunca es undefined. input(defecto) infiere el tipo del valor por defecto. Ambos devuelven señales de solo lectura: las lees con (), pero no tienen .set() —el padre es quien las cambia—.

Derivar y reaccionar a un input (adiós ngOnChanges)

Como el input es una señal, puedes construir computed y effect sobre él con total naturalidad:

tarjeta.component.ts
import { Component, input, computed, effect } from '@angular/core';

@Component({ selector: 'app-tarjeta-gasto', template: `...` })
export class TarjetaGastoComponent {
valor = input.required<number>();

// derivado del input: se recalcula solo cuando el padre cambia 'valor'
conIva = computed(() => Math.round(this.valor() * 1.19));

constructor() {
  // reacciona a cambios del input SIN ngOnChanges
  effect(() => console.log('El valor cambió a', this.valor()));
}
}

Aquí está el paralelo legacy→moderno más útil de este tema:

Transformar el valor de entrada

input() admite una transformación que normaliza el valor al entrar —útil para coerción:

transform.ts
import { input, numberAttribute, booleanAttribute } from '@angular/core';

export class Ej {
// el padre puede pasar "42" (string de un atributo) y aquí llega como number
valor = input(0, { transform: numberAttribute });
// "" o presencia del atributo → true
activo = input(false, { transform: booleanAttribute });
// transform propio
nombre = input('', { transform: (v: string) => v.trim().toUpperCase() });
}

model(): two-way binding reactivo

model() crea una señal que el hijo puede LEER y ESCRIBIR, y cuyos cambios el padre recibe automáticamente. Es la base para componentes de formulario reutilizables:

campo-monto.component.ts
import { Component, model } from '@angular/core';

@Component({
selector: 'app-campo-monto',
template: `
  <button (click)="monto.set(monto() - 500)">−</button>
  <span>{{ monto() }}</span>
  <button (click)="monto.set(monto() + 500)">+</button>
`,
})
export class CampoMontoComponent {
monto = model(0);   // señal de lectura Y escritura, sincronizada con el padre
}
// El padre:  <app-campo-monto [(monto)]="presupuesto" />
// Cuando el hijo hace monto.set(...), la señal 'presupuesto' del padre se actualiza.

A diferencia de input() (solo lectura), model() sí tiene .set() y .update(), y su sintaxis en el padre es la "banana en caja" [(monto)]. Úsalo cuando el hijo necesite proponer cambios al padre —un control de cantidad, un campo de texto, un toggle—.

Practica la lógica reactiva en vivo

Los inputs de Angular no corren en el sandbox, pero su ESENCIA —una señal externa de la que derivas— sí. Con tu mini-sistema de señales, simula un input valor que "el padre" cambia, y un derivado conIva que reacciona:

¿Qué reemplaza a `ngOnChanges` cuando quieres reaccionar a cambios de un `input()` señal?

Mini-reto

En el playground: 1) haz un valorInput (señal) y un presupuestoInput (señal); 2) crea un computado saldo = presupuesto - valor; 3) simula que "el padre" cambia ambos inputs con varios set y verifica que saldo se recalcula en cada caso. Luego, en tu cabeza, escribe cómo se vería esto como un componente Angular con dos input() y un computed().

Qué sigue

Dominas los inputs reactivos. Quedan dos herramientas avanzadas de señales: linkedSignal (un estado derivado que TAMBIÉN puedes escribir) y resource() (para cargar datos async de forma declarativa, con estados de carga/error como señales). La próxima lección las cubre y cierra la teoría de este módulo.