Angular 21 de Cero a Experto / Tiempo real y sistemas distribuidos
Socket.io con Angular: eventos con nombre y un socket vivo
Socket.io es la librería de tiempo real más usada: añade sobre el WebSocket todo lo que el nativo no trae —reconexión automática, fallback, salas y un modelo de eventos con nombre—. Hoy aprendes a integrarla en Angular con un servicio de socket envuelto en señales, y construyes un SOCKET SIMULADO que corre en vivo con el mismo modelo mental —así entiendes el patrón sin necesitar un servidor real—.
El operador de la centralita
Eventos con nombre: on y emit
El modelo de Socket.io son eventos con NOMBRE —un pub/sub como el que viste en el SC-02—:
import { io } from 'socket.io-client';
const socket = io('wss://api.mibolsillo.com');
// ESCUCHAR un evento con nombre
socket.on('gasto:nuevo', (gasto: Gasto) => {
this.gastos.update((gs) => [...gs, gasto]);
});
socket.on('connect', () => console.log('conectado', socket.id));
socket.on('disconnect', () => console.log('desconectado'));
// EMITIR un evento con nombre (+ datos)
socket.emit('gasto:crear', { nombre: 'Café', valor: 4500 });Frente al WebSocket nativo (un solo onmessage donde discriminas por tipo a mano), Socket.io
te da eventos con nombre directamente: socket.on('gasto:nuevo', ...). Es el mismo modelo
pub/sub del store observable del SC-02 —suscribirse a un evento con nombre y reaccionar—. Y la
reconexión, el fallback y el heartbeat vienen incluidos.
Envolverlo en un servicio Angular
La clave en Angular es CERRAR bien la conexión y quitar los listeners —igual que el cleanup de efectos que ya dominas—:
import { Injectable, signal, OnDestroy } from '@angular/core';
import { io, Socket } from 'socket.io-client';
@Injectable({ providedIn: 'root' })
export class SocketService implements OnDestroy {
private socket: Socket = io('wss://api.mibolsillo.com', { autoConnect: false });
readonly conectado = signal(false);
readonly gastos = signal<Gasto[]>([]);
conectar() {
this.socket.connect();
this.socket.on('connect', () => this.conectado.set(true));
this.socket.on('disconnect', () => this.conectado.set(false));
this.socket.on('gasto:nuevo', (g: Gasto) => this.gastos.update((gs) => [...gs, g]));
}
crearGasto(nombre: string, valor: number) {
this.socket.emit('gasto:crear', { nombre, valor });
}
ngOnDestroy() {
this.socket.off(); // quita TODOS los listeners (evita duplicados/fugas)
this.socket.disconnect();
}
}Dos detalles críticos: socket.off() quita los listeners al destruir (si no, al reconectar se
acumulan y los mensajes llegan duplicados —el mismo bug de suscripciones que en React SC-06 M09—),
y disconnect() cierra la conexión. Los eventos entrantes alimentan señales, y la UI reacciona.
Un socket simulado, en vivo
No podemos correr un servidor Socket.io en el sandbox, pero su MODELO —on/emit/off con
eventos con nombre— es un objeto en memoria. Aquí, un socket simulado con eco y un "bot" que
empuja eventos, corriendo de verdad:
Observa el modelo: te suscribes a gasto:nuevo con on, emites gasto:crear con emit, y el
"servidor" te responde con un gasto:nuevo —además de los empujones del bot (otros usuarios)—.
Es EXACTAMENTE el API de Socket.io. En Angular real, solo cambiarías new SocketSimulado() por
io(url) y estos eventos alimentarían señales. El off() al final quita los listeners: el
mismo cleanup que evita duplicados.
¿Por qué es crucial llamar `socket.off()` (quitar listeners) al destruir el componente/servicio?
Mini-reto
En el playground, amplía el socket simulado: 1) añade un evento gasto:borrar que el cliente
emite y el servidor confirma con gasto:borrado (el id); 2) suscríbete a gasto:borrado y
resta del total; 3) haz que el bot ocasionalmente emita usuario:conectado con un nombre, y
loguéalo. Confirma que quitar los listeners con off() detiene todo limpiamente.
Qué sigue
Ya manejas eventos con nombre y un socket vivo. Falta lo que hace robusto el tiempo real en el mundo real: qué pasa cuando la conexión se CAE y cómo no ahogarse cuando llegan demasiados mensajes. La próxima lección son la reconexión y el backpressure —las estrategias que mantienen tu app viva bajo condiciones reales de red—.