Angular 21 de Cero a Experto / Signals a fondo
linkedSignal y resource(): estado derivado y datos async
Cierras la teoría de señales con dos herramientas que resuelven problemas del mundo real.
linkedSignal es para cuando quieres un valor que normalmente se DERIVA de otro, pero que
el usuario también puede sobrescribir (y que se "resetea" cuando la fuente cambia).
resource() es la joya para datos asíncronos: cargar de una API con estados de carga, error
y valor —todos como señales— de forma declarativa. Hoy las conoces y sabes cuándo usar cada
una.
linkedSignal: derivado pero escribible
Un computed es de solo lectura: no puedes escribirle. Pero a veces quieres un valor que
POR DEFECTO se deriva de otra señal, y que el usuario pueda cambiar temporalmente —hasta que
la fuente cambie y lo resetee—.
import { Component, signal, linkedSignal } from '@angular/core';
@Component({ selector: 'app-form-gasto', template: `...` })
export class FormGastoComponent {
comercio = signal<'super' | 'gasolinera' | 'cine'>('super');
// categoría: derivada del comercio, pero el usuario puede sobrescribirla
categoria = linkedSignal(() => {
switch (this.comercio()) {
case 'super': return 'comida';
case 'gasolinera': return 'transporte';
case 'cine': return 'ocio';
}
});
// el usuario la cambia a mano: this.categoria.set('otro')
// pero si cambia 'comercio', se recalcula desde la fuente (se resetea)
}La diferencia con computed: computed NUNCA se puede escribir (siempre refleja el
cálculo); linkedSignal se puede escribir (set/update), pero vuelve a derivarse cuando
su fuente cambia. Es el punto medio entre "señal libre" y "señal derivada".
resource(): datos asíncronos como señales
Cargar datos de una API tiene siempre los mismos tres estados: cargando, error, y datos.
resource() los empaqueta en señales y vuelve a cargar automáticamente cuando cambia un
parámetro de entrada:
import { Component, input, resource } from '@angular/core';
@Component({
selector: 'app-detalle-gasto',
template: `
@if (gastoRes.isLoading()) {
<p>Cargando…</p>
} @else if (gastoRes.error()) {
<p>Error al cargar el gasto.</p>
} @else {
<h3>{{ gastoRes.value()?.nombre }}</h3>
}
`,
})
export class DetalleGastoComponent {
id = input.required<number>();
gastoRes = resource({
params: () => ({ id: this.id() }), // cuando 'id' cambia, recarga
loader: async ({ params }) => {
const res = await fetch(`/api/gastos/${params.id}`);
if (!res.ok) throw new Error('Falló la carga');
return res.json();
},
});
}resource() te da señales listas para el template:
isLoading()—truemientras carga.error()— el error si el loader lanzó.value()— los datos cuando llegan.reload()— método para recargar a mano.
Y lo mejor: si id (una señal en params) cambia, resource vuelve a ejecutar el loader
solo. Es reactividad aplicada a datos remotos. Si esto te recuerda a useQuery de TanStack
en React, vas bien encaminado: mismo problema, solución integrada en Angular.
Antes (subscribe manual) vs Ahora (resource)
¿Cuándo usarías `linkedSignal` en lugar de `computed`?
Mini-reto
Sin código ejecutable (estas APIs viven en Angular): 1) describe un caso de tu app "Mi
Bolsillo" donde linkedSignal encaje mejor que computed —piensa en un valor autorrellenado
que el usuario pueda corregir—; 2) escribe el esqueleto de un resource() que cargue la
lista de gastos de un mes, con params dependiendo de una señal mes; 3) enumera las tres
señales que expondrías en el template para cargando/error/datos.
Qué sigue
Cierras la teoría de señales con las siete herramientas: signal, computed, effect,
signal inputs, model, linkedSignal y resource. Es hora del reto que corona el módulo:
vas a construir tu PROPIO sistema de señales completo —signal, computed y effect con
rastreo de dependencias real— desde cero, en vivo. Entenderás Angular por dentro como pocos.