Fundamento teórico de linkedSignal
La función linkedSignal representa una evolución natural en el ecosistema de signals de Angular, diseñada para resolver una limitación fundamental que encontramos con los computed signals: la imposibilidad de modificar su valor directamente.
La necesidad de linkedSignal
Cuando trabajamos con computed signals, nos enfrentamos a una restricción importante: son de solo lectura. Un computed signal se recalcula automáticamente cuando cambian sus dependencias, pero no podemos asignarle un valor directamente. Esta característica, aunque útil para muchos casos, se convierte en un obstáculo cuando necesitamos:
- Sincronización bidireccional entre diferentes fuentes de estado
- Derivar valores que ocasionalmente necesitan ser sobrescritos manualmente
- Combinar estado reactivo con actualizaciones imperativas
// Computed signal: solo lectura
const fullName = computed(() => `${firstName()} ${lastName()}`);
// fullName.set('John Smith'); // ❌ Error: no es posible
Qué es linkedSignal
linkedSignal es un primitivo reactivo que combina lo mejor de ambos mundos: la capacidad de derivarse automáticamente de otras signals (como computed) y la flexibilidad de ser modificado directamente (como un WritableSignal normal).
Esta función nos permite crear signals que:
- Se actualizan automáticamente cuando cambian sus dependencias
- Pueden ser modificados manualmente usando métodos como set() y update()
- Mantienen sincronización compleja entre múltiples fuentes de estado
- Proporcionan control granular sobre cuándo aplicar la derivación automática
Diferencias clave con computed
La principal diferencia conceptual entre computed y linkedSignal radica en el control de escritura:
| Característica | computed() | linkedSignal() | |----------------|------------|----------------| | Lectura reactiva | ✅ Sí | ✅ Sí | | Escritura directa | ❌ No | ✅ Sí | | Auto-derivación | ✅ Automática | ✅ Configurable | | Casos de uso | Valores calculados puros | Estado sincronizado complejo |
Casos de uso principales
Los linkedSignals brillan en escenarios donde necesitamos flexibilidad en la gestión de estado:
Sincronización de formularios: Cuando un campo puede calcularse automáticamente pero también permite entrada manual del usuario.
Cache inteligente: Estado que se deriva de fuentes remotas pero puede ser actualizado localmente para optimizar la experiencia de usuario.
Estado de UI complejo: Elementos de interfaz que responden tanto a cambios automáticos como a interacciones directas del usuario.
Configuración dinámica: Parámetros que tienen valores por defecto calculados pero pueden ser personalizados manualmente.
Modelo mental para linkedSignal
Para entender linkedSignal, es útil pensar en él como un signal que tiene dos "modos de operación":
- Modo derivado: Se comporta como un computed signal, actualizándose cuando cambian sus dependencias
- Modo manual: Se comporta como un WritableSignal normal, permitiendo asignaciones directas
La clave está en que linkedSignal nos da control sobre cuándo y cómo alternar entre estos modos, permitiendo sincronización sofisticada de estado que sería compleja de implementar con otros primitivos.
Esta flexibilidad hace de linkedSignal una herramienta indispensable para casos donde el estado necesita ser tanto reactivo como imperativo, proporcionando la base para patrones de sincronización avanzados en aplicaciones Angular modernas.
Creación de linkedSignal
La creación de linkedSignals en Angular sigue un patrón específico que combina la definición de la lógica de derivación con la configuración del comportamiento reactivo. Veamos cómo implementar estos primitivos avanzados en nuestros componentes.
Sintaxis básica
La función linkedSignal acepta una función de computación como primer parámetro, similar a computed, pero con la diferencia fundamental de que retorna un WritableSignal:
import { linkedSignal } from '@angular/core';
// Sintaxis básica
const myLinkedSignal = linkedSignal(() => {
// Lógica de derivación
return someValue;
});
A diferencia de computed, el linkedSignal nos permite posteriormente usar métodos como set()
y update()
para modificar su valor manualmente.
Parámetros de configuración
La función linkedSignal acepta un objeto de configuración como segundo parámetro opcional, que nos permite personalizar su comportamiento:
const linkedValue = linkedSignal(
() => sourceSignal() * 2, // Función de computación
{
equal: (a, b) => a === b, // Función de comparación personalizada
}
);
Ejemplo práctico: Campo de búsqueda inteligente
Creemos un linkedSignal que deriva automáticamente un término de búsqueda de la URL, pero permite sobrescritura manual:
@Component({
selector: 'app-search',
template: `
<input
[value]="searchTerm()"
(input)="onSearchInput($event)"
placeholder="Buscar productos...">
@if (searchTerm()) {
<p>Buscando: {{ searchTerm() }}</p>
}
`
})
export class SearchComponent {
private route = inject(ActivatedRoute);
// Signal que deriva de los query params de la ruta
searchTerm = linkedSignal(() => {
return this.route.snapshot.queryParams['q'] || '';
});
onSearchInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
// Podemos actualizar manualmente el linkedSignal
this.searchTerm.set(value);
}
}
Sincronización con múltiples fuentes
Los linkedSignals brillan cuando necesitamos derivar de múltiples signals pero mantener flexibilidad de escritura:
@Component({
selector: 'app-user-profile',
template: `
<input
[value]="displayName()"
(input)="updateDisplayName($event)">
<button (click)="resetToDefault()">
Usar nombre automático
</button>
`
})
export class UserProfileComponent {
firstName = signal('Juan');
lastName = signal('Pérez');
// LinkedSignal que deriva del nombre completo por defecto
displayName = linkedSignal(() => {
const first = this.firstName();
const last = this.lastName();
return `${first} ${last}`;
});
updateDisplayName(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.displayName.set(value);
}
resetToDefault() {
// Forzamos recálculo desde las fuentes originales
this.displayName.set(
`${this.firstName()} ${this.lastName()}`
);
}
}
Configuración avanzada con equal
Podemos personalizar cuándo el linkedSignal considera que ha cambiado su valor:
interface User {
id: number;
name: string;
email: string;
}
@Component({...})
export class UserManagerComponent {
users = signal<User[]>([]);
selectedUserId = signal<number | null>(null);
// LinkedSignal con comparación personalizada
selectedUser = linkedSignal(
() => {
const id = this.selectedUserId();
return this.users().find(user => user.id === id) || null;
},
{
// Solo actualiza si cambia el ID del usuario
equal: (a, b) => a?.id === b?.id
}
);
selectUser(userId: number) {
this.selectedUserId.set(userId);
}
// Permitir establecer directamente un usuario
setCustomUser(user: User) {
this.selectedUser.set(user);
}
}
LinkedSignal con transformaciones
Podemos crear linkedSignals que aplican transformaciones complejas:
@Component({
selector: 'app-price-calculator',
template: `
<input
type="number"
[value]="basePrice()"
(input)="updateBasePrice($event)">
<select (change)="updateTax($event)">
<option value="0.21">IVA 21%</option>
<option value="0.10">IVA 10%</option>
<option value="0.04">IVA 4%</option>
</select>
<input
type="number"
[value]="finalPrice()"
(input)="setFinalPrice($event)"
placeholder="Precio final manual">
<p>Precio base: {{ basePrice() }}€</p>
<p>Precio final: {{ finalPrice() }}€</p>
`
})
export class PriceCalculatorComponent {
basePrice = signal(100);
taxRate = signal(0.21);
// LinkedSignal que calcula precio final con impuestos
finalPrice = linkedSignal(() => {
const base = this.basePrice();
const tax = this.taxRate();
return Number((base * (1 + tax)).toFixed(2));
});
updateBasePrice(event: Event) {
const value = +(event.target as HTMLInputElement).value;
this.basePrice.set(value);
}
updateTax(event: Event) {
const value = +(event.target as HTMLSelectElement).value;
this.taxRate.set(value);
}
setFinalPrice(event: Event) {
const value = +(event.target as HTMLInputElement).value;
// Override manual del precio calculado
this.finalPrice.set(value);
}
}
Gestión del estado de sincronización
Una práctica común es rastrear si el linkedSignal está en modo "manual" o "automático":
@Component({
selector: 'app-smart-field',
template: `
<div class="field-container">
<input
[value]="computedValue()"
(input)="handleManualInput($event)">
@if (isManualOverride()) {
<button (click)="resetToAuto()">
🔄 Volver a automático
</button>
}
</div>
`
})
export class SmartFieldComponent {
sourceValue = signal('valor inicial');
isManualOverride = signal(false);
computedValue = linkedSignal(() => {
// Solo aplica transformación automática si no hay override
if (!this.isManualOverride()) {
return this.sourceValue().toUpperCase();
}
return this.computedValue(); // Mantiene valor actual
});
handleManualInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.computedValue.set(value);
this.isManualOverride.set(true);
}
resetToAuto() {
this.isManualOverride.set(false);
// Forzar recálculo automático
this.computedValue.set(this.sourceValue().toUpperCase());
}
}
La creación de linkedSignals nos proporciona una herramienta flexible para manejar estado que necesita ser tanto reactivo como modificable directamente, permitiendo implementar patrones de sincronización sofisticados que serían complejos con otros primitivos de Angular.

Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Angular
Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender la limitación de los computed signals y la necesidad de linkedSignal.
- Aprender la sintaxis y configuración básica de linkedSignal.
- Identificar casos de uso donde linkedSignal es más adecuado que computed.
- Implementar linkedSignals que combinan derivación automática y modificaciones manuales.
- Gestionar estados complejos y sincronización bidireccional con linkedSignal.