Aplicação de Chat com Angular e Socket.IO

Introdução

Usaremos RxJS, Angular, Express (Node.js) &Socket.IO para fazer uma aplicação de chat. Vamos ver o quão útil o RxJS pode ser neste cenário. Com o propósito de tornar as coisas mais suaves e fáceis estaremos usando Angular CLI para gerar a estrutura básica do cliente e nos obter uma placa de caldeira para a aplicação Angular mais simples de trabalhar. No back-end usaremos Node.js com express e Socket.IO. A razão por detrás disto é que o Socket.IO é muito fácil de configurar e de trabalhar. Além disso, ele fornece bibliotecas tanto do lado do servidor como do lado do cliente. Socket.IO usa principalmente o protocolo WebSocket para permitir a comunicação bidirecional em tempo real.

Se você quiser pular diretamente para a parte onde usamos RxJS para lidar com as mensagens de chat, clique aqui.

Vamos começar!

Socket.Configuração do servidor IO

Se você não tem o CLI Angular instalado você pode instalá-lo facilmente:

npm i -g @angular/cli

Configurar estrutura de pastas

Para manter as coisas limpas vamos criar uma nova pasta rxjs-chat para o nosso pequeno projeto. Deve servir como pasta raiz para Angular e Socket.IO apps.

Vamos criar uma aplicação Angular básica usando Angular CLI. Em nossa pasta de projetos execute seu terminal favorito e execute o seguinte comando:

ng new rxjs-chat

Dê-lhe um pouco de tempo. Depois de ter criado a estrutura básica e instalado os pacotes npm, renomearemos para cliente a pasta de projeto Angular recém-criada. Depois disso, na mesma pasta raiz criamos o servidor de pastas para o nosso servidor Socket.IO. Agora temos duas pastas dentro da nossa pasta rxjs-chat:

  • client – Angular client app
  • server – em breve será Node’s Socket.IO server
Instalando pacotes locais

Na pasta server vamos gerar o pacote.json executando o seguinte comando:

npm init -y

Depois disso instalaremos nossas dependências e as salvaremos em package.json:

npm install express socket.io --save

Na mesma pasta criaremos o arquivo index.js para a aplicação do nosso servidor. O conteúdo do arquivo deve ser o seguinte:

Criamos uma instância de express e armazenamo-la em app variável. Depois disso criamos o servidor com o módulo http. Depois passamos por express para http.Server() método. Express servirá como o manipulador de pedidos para o nosso servidor. Em troca obtemos a instância do servidor que armazenamos em server variável.

Nas duas linhas de código seguintes ligamos o socket.IO com o nosso http server:

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

let io = socketIO(server);

Depois ouvimos o evento de conexão do socket.IO e fazemos o log uma vez que um usuário tenha estabelecido uma conexão com o servidor. Finalmente iniciamos o servidor e ouvimos em determinada porta, no nosso caso é a porta 3000.
Vamos realmente iniciar o nosso servidor executando o seguinte na pasta server:
node index.js
Consequentemente você deve receber a seguinte mensagem: “iniciado na porta”: 3000″.

Conectando ao Socket.IO

Temos um servidor rodando na porta 3000. Agora é altura de ver como nos podemos ligar ao servidor a partir da nossa aplicação Angular. Para isso precisaremos instalar a biblioteca cliente Socket.IO. Vamos executar o seguinte comando na nossa pasta cliente:

npm install socket.io-client --save

>Por enquanto estamos nela vamos executar a aplicação Angular via Angular CLI:

ng serve -o

Agora temos a nossa aplicação em execução. Podemos ver que o nosso navegador padrão acabou de abrir uma nova aba apontando para o localhost. É por isso que usamos -o argumento para abrir o navegador.

Criar serviço para comunicação com o servidor

I’am gonna name it chat.service.ts app.chat.service.ts e colocá-lo dentro da pasta src. O serviço vai ser o mais simples possível por enquanto:

Adicionando o serviço aos provedores de módulo

Ainda isso, vamos adicionar nosso ChatService aos provedores dentro do AppModule no arquivo app.module.ts:

Injetando o serviço em nosso componente

Finalmente, vamos importar e usar o serviço dentro do nosso componente app.part.ts file:

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

E vamos injetar o ChatService em nosso construtor:

constructor(chatService: ChatService) { }

Como resultado, temos o seguinte arquivo:

Se tudo correu como esperado após salvar as alterações você deve ver a mensagem ‘user connected’ no terminal onde você iniciou o nodo app:

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

Hence, podemos dizer que finalmente conseguimos nos conectar ao nosso servidor Socket IO. Agora é hora de realmente enviar alguns dados via Socket IO events.

Enviar uma mensagem para o Socket.IO

Tempo para melhorar o nosso até agora simples ChatService com uma opção para enviar uma mensagem para o servidor:

Para isto funcionar temos de usar sendMessage() do nosso AppComponent:

Finalmente, vamos fornecer ao utilizador uma simples entrada de texto e um botão para que ele possa enviar uma mensagem:

Para que tudo isto funcione, temos de ouvir no servidor o evento ‘new-message’ que emitimos do ChatService:

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

Agora o nosso índice.js se parece com isto:

Se você quiser brincar com o código você pode encontrá-lo no meu repo GitHub – rxjs-chat.

Comunicando com outros usuários

Para que a nossa mensagem seja transmitida para outros usuários tudo o que temos que fazer é mudar a console.log(message) na parte da nossa chamada de retorno do evento de ‘nova mensagem’ para io.emit(message):

E o que o io.emit() faz? Em palavras simples ele envia um evento para todos conectados ao servidor.

Vamos nos comunicar com o nosso servidor via ChatService. Vamos adicionar um novo método ao nosso serviço para este fim – getMessages. Ele retornará um Observável que nós criaremos com o método Observable.create(). Toda vez que o socket receber uma nova mensagem nós usaremos observer.next() para encaminhá-la aos observadores.

Após isso nós assinamos este Observável a partir de nosso AppComponent:

Criamos um novo messages array onde armazenaremos as mensagens recebidas do servidor. Nós teremos que mostrar essas mensagens no nosso template. Este é o caso perfeito para ngFor:

.