Angular Signals es una de las características más recientes del ecosistema de Angular, diseñada para mejorar la reactividad y la eficiencia en la gestión de estado. En aplicaciones con flujos complejos, la correcta implementación de signals permite una arquitectura más predecible, reduciendo renders innecesarios y mejorando el rendimiento.
En este artículo exploraremos cómo usar Angular Signals en escenarios avanzados, incluyendo su aplicación en gestión de estado, comunicación entre componentes y sincronización de datos asíncronos.
¿Qué son los Signals en Angular?
Los Signals en Angular proporcionan una forma declarativa y eficiente de manejar datos reactivos. A diferencia de RxJS
, los signals permiten una actualización automática y determinista del estado sin necesidad de suscripciones explícitas.
Ventajas de Signals en Angular
- ✅ Actualización automática: Reactualizan valores cuando su dependencia cambia.
- ✅ Menor complejidad: No requieren suscripciones manuales.
- ✅ Rendimiento optimizado: Reducen renders innecesarios.
- ✅ Compatibles con computación derivada: Se pueden encadenar para formar estructuras reactivas más complejas.
Ejemplo básico de un Signal en Angular:
import { signal } from '@angular/core';
export class ContadorComponent {
contador = signal(0);
incrementar() {
this.contador.set(this.contador() + 1);
}
}
1. Gestión de Estado Global con Signals
En aplicaciones con flujos complejos, es fundamental manejar el estado de manera centralizada y eficiente. Los Signals permiten crear una solución sencilla para compartir estado entre múltiples componentes.
Implementación de un Store con Signals
Creamos un servicio global para gestionar el estado de un carrito de compras.
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class CarritoService {
private productos = signal<{ id: number, nombre: string, cantidad: number }[]>([]);
agregarProducto(producto: { id: number, nombre: string }) {
this.productos.set([...this.productos(), { ...producto, cantidad: 1 }]);
}
obtenerCarrito() {
return this.productos;
}
}
En un componente, podemos suscribirnos al estado de esta forma:
import { Component, inject } from '@angular/core';
import { CarritoService } from './carrito.service';
@Component({
selector: 'app-carrito',
template: `
<ul>
<li *ngFor="let item of carrito()">{{ item.nombre }} ({{ item.cantidad }})</li>
</ul>
`
})
export class CarritoComponent {
carrito = inject(CarritoService).obtenerCarrito();
}
✅ Beneficio: No es necesario usar RxJS
ni BehaviorSubject
, reduciendo la complejidad.
2. Comunicación entre Componentes con Signals
En aplicaciones modulares, los Signals facilitan la comunicación eficiente entre componentes sin necesidad de @Input()
y @Output()
.
Ejemplo: Estado Compartido entre Componentes Hermanos
1️⃣ Servicio Compartido
import { Injectable, signal } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class EstadoService {
contador = signal(0);
}
2️⃣ Componente que Modifica el Estado
import { Component, inject } from '@angular/core';
import { EstadoService } from './estado.service';
@Component({
selector: 'app-incrementador',
template: `<button (click)="incrementar()">Incrementar</button>`
})
export class IncrementadorComponent {
estadoService = inject(EstadoService);
incrementar() {
this.estadoService.contador.set(this.estadoService.contador() + 1);
}
}
3️⃣ Componente que Muestra el Estado
import { Component, inject } from '@angular/core';
import { EstadoService } from './estado.service';
@Component({
selector: 'app-contador',
template: `<p>Contador: {{ contador() }}</p>`
})
export class ContadorComponent {
contador = inject(EstadoService).contador;
}
✅ Beneficio: Los componentes comparten estado sin necesidad de event bindings manuales.
3. Manejo de Datos Asíncronos con Signals
Los Signals pueden manejar llamadas HTTP y actualizar automáticamente la UI.
Ejemplo: Llamada a una API con Signals
import { Component, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-usuarios',
template: `
<ul>
<li *ngFor="let usuario of usuarios()">{{ usuario.name }}</li>
</ul>
`
})
export class UsuariosComponent {
private http = inject(HttpClient);
usuarios = signal<{ name: string }[]>([]);
constructor() {
this.http.get<{ name: string }[]>('https://jsonplaceholder.typicode.com/users')
.subscribe(data => this.usuarios.set(data));
}
}
✅ Beneficio: Se eliminan suscripciones manuales y async pipe
, mejorando la simplicidad.
4. Optimización del Renderizado con Signals
Signals minimizan renders innecesarios. En comparación con @Input()
, los Signals permiten actualizar solo la parte del estado que cambia.
Ejemplo: Renderización Optimizada
import { Component, Input, signal } from '@angular/core';
@Component({
selector: 'app-producto',
template: `<p>{{ producto().nombre }} - {{ producto().precio }}</p>`
})
export class ProductoComponent {
@Input() producto = signal({ nombre: '', precio: 0 });
}
✅ Beneficio: Solo se renderiza la parte del estado que cambia, mejorando la eficiencia.
Conclusión
Los Signals en Angular proporcionan un mecanismo moderno y eficiente para manejar flujos de datos complejos. Su integración con la arquitectura de Angular mejora la legibilidad, el rendimiento y la mantenibilidad del código.