Aplicación de chat con Angular y Socket.IO

Introducción

Usaremos RxJS, Angular, express (Node.js) & Socket.IO para hacer una aplicación de chat. Llegaremos a ver lo útil que puede ser RxJS en este escenario. Con el fin de hacer las cosas fáciles y sin problemas vamos a utilizar Angular CLI para generar la estructura básica del cliente y obtener un boilerplate para la aplicación Angular más simple de trabajo. En el back-end usaremos Node.js con express y Socket.IO. La razón detrás de esto es que Socket.IO es muy fácil de configurar y trabajar. Además, proporciona librerías tanto del lado del servidor como del lado del cliente. Socket.IO utiliza principalmente el protocolo WebSocket para permitir la comunicación bidireccional en tiempo real.

Si quieres saltar directamente a la parte en la que usamos RxJS para manejar los mensajes del chat haz clic aquí.

¡Comencemos!

Socket.IO server setup

Si no tienes Angular CLI instalado puedes instalarlo muy fácilmente:

npm i -g @angular/cli

Set up folder structure

Para mantener las cosas limpias vamos a crear una nueva carpeta rxjs-chat para nuestro pequeño proyecto. Debería servir como carpeta raíz para las aplicaciones Angular y Socket.IO.

Creemos una aplicación Angular básica usando Angular CLI. En nuestra carpeta de proyecto lanza tu terminal favorito y ejecuta el siguiente comando:

ng new rxjs-chat

Dale un poco de tiempo. Una vez que haya terminado de crear la estructura básica y de instalar los paquetes npm, cambiaremos el nombre de la carpeta del proyecto Angular recién creado por el de cliente. Después, en la misma carpeta raíz crearemos la carpeta server para nuestro servidor Socket.IO. Ahora tenemos dos carpetas dentro de nuestra carpeta rxjs-chat:

  • client – Angular client app
  • server – soon to be Node’s Socket.IO server
Installing local packages

En la carpeta server generaremos el archivo package.json ejecutando el siguiente comando:

npm init -y

Después instalaremos nuestras dependencias y las guardaremos en package.json:

npm install express socket.io --save

En la misma carpeta crearemos el archivo index.js para nuestra app del servidor. El contenido del archivo debe ser el siguiente:

Creamos una instancia de express y la almacenamos en la variable app. Después creamos el servidor con el módulo http. Luego pasamos express al método http.Server(). Express servirá como manejador de las peticiones a nuestro servidor. A cambio obtenemos la instancia del servidor que almacenamos en la variable server.

En las siguientes dos líneas de código enlazamos el socket.IO con nuestro http server:

let socketIO = require('socket.io');

let io = socketIO(server);

Después escuchamos el evento de conexión del socket.IO y registramos una vez que un usuario ha establecido una conexión con el servidor. Finalmente iniciamos el servidor y escuchamos en el puerto dado, en nuestro caso es el puerto 3000.
En realidad vamos a iniciar nuestro servidor ejecutando lo siguiente en la carpeta del servidor:
node index.js
Consecuentemente deberías obtener el siguiente mensaje: «iniciado en el puerto: 3000».

Conectando a Socket.IO

Tenemos un servidor funcionando en el puerto 3000. Ahora es el momento de ver cómo podemos conectarnos al servidor desde nuestra app Angular. Para ello necesitaremos instalar la librería cliente Socket.IO. Ejecutaremos el siguiente comando en nuestra carpeta cliente:

npm install socket.io-client --save

Mientras tanto vamos a ejecutar la app de Angular a través del CLI de Angular:

ng serve -o

Ahora tenemos nuestra app corriendo. Podemos ver que nuestro navegador por defecto acaba de abrir una nueva pestaña apuntando a localhost. Por eso hemos utilizado el argumento -o para abrir el navegador.

Creando el servicio para la comunicación con el servidor

Voy a llamarlo chat.service.ts app.chat.service.ts y lo voy a colocar dentro de la carpeta src. El servicio va a ser lo más sencillo posible por ahora:

Añadir el servicio a los proveedores del módulo

Además, vamos a añadir nuestro ChatService a la lista de proveedores dentro de AppModule en el archivo app.module.ts:

Inyectar el servicio en nuestro componente

Por último, vamos a importar y utilizar el servicio dentro de nuestro app.component.ts:

import { ChatService } from './app.chat.service';

Y vamos a inyectar el ChatService en nuestro constructor:

constructor(chatService: ChatService) { }

Como resultado tenemos el siguiente archivo:

Si todo ha ido como se esperaba después de guardar los cambios deberías ver el mensaje de ‘usuario conectado’ en el terminal donde iniciaste la aplicación node:

$ node index.js
started on port: 3000
user connected

Por lo tanto, podemos decir que por fin hemos conseguido conectarnos a nuestro servidor Socket IO. Ahora es el momento de enviar realmente algunos datos a través de los eventos de Socket IO.

Enviar un mensaje a Socket.IO

Es hora de mejorar nuestro hasta ahora simple ChatService con una opción para enviar un mensaje al servidor:

Para que esto funcione tenemos que utilizar sendMessage() de nuestro AppComponent:

Por último, vamos a proporcionar al usuario una entrada de texto simple y un botón para que pueda enviar un mensaje:

Para que todo esto funcione tenemos que escuchar en el servidor el evento ‘new-message’ que emitimos desde ChatService:

socket.on('new-message', (message) => {
console.log(message);
});

Ahora nuestro index.js se ve así:

Si quieres jugar con el código puedes encontrarlo en mi repo de GitHub – rxjs-chat.

Comunicarse con otros usuarios

Para que nuestro mensaje se emita a otros usuarios todo lo que tenemos que hacer es cambiar el console.log(message) dentro de nuestro callback de evento ‘new-message’ a io.emit(message):

¿Y qué hace io.emit()? En palabras sencillas envía un evento a todos los conectados al servidor.

Vamos a comunicarnos con nuestro servidor a través de ChatService. Vamos a añadir un nuevo método a nuestro servicio para este propósito – getMessages. Devolverá un Observable que crearemos con el método Observable.create(). Cada vez que el socket reciba un nuevo mensaje usaremos observer.next() para reenviarlo a los observadores.

Después nos suscribimos a este Observable desde nuestra AppComponent:

Creamos un nuevo messages array donde almacenaremos los mensajes recibidos del servidor. Tendremos que mostrar esos mensajes en nuestra plantilla. Este es un caso de uso perfecto para ngFor: