CodeForge

Angular 21 de Cero a Experto / Tiempo real y sistemas distribuidos

Socket.io con Angular: eventos con nombre y un socket vivo

Teoría24 min20 XP

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—:

socket.ts
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—:

socket.service.ts
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—.