Aplikacja czatowa z Angular i Socket.IO

Wprowadzenie

Użyjemy RxJS, Angular, express (Node.js) & Socket.IO do stworzenia aplikacji czatowej. Przekonamy się jak przydatny może być RxJS w tym scenariuszu. Aby wszystko było gładkie i łatwe będziemy używać Angular CLI do generowania podstawowej struktury klienta i uzyskania boilerplate dla najprostszej działającej aplikacji Angular. Na back-end użyjemy Node.js z express i Socket.IO. Powodem tego jest to, że Socket.IO jest bardzo łatwy do skonfigurowania i pracy z nim. Co więcej, dostarcza zarówno biblioteki po stronie serwera jak i klienta. Socket.IO przede wszystkim wykorzystuje protokół WebSocket, aby umożliwić dwukierunkową komunikację w czasie rzeczywistym.

Jeśli chcesz przeskoczyć bezpośrednio do części, w której używamy RxJS do obsługi wiadomości czatu, kliknij tutaj.

Zaczynamy!

Socket.IO

Jeśli nie masz zainstalowanego Angular CLI, możesz go zainstalować w bardzo prosty sposób:

npm i -g @angular/cli

Ustaw strukturę folderów

Aby zachować porządek, utworzymy nowy folder rxjs-chat dla naszego małego projektu. Powinien on służyć jako folder główny dla aplikacji Angular i Socket.IO.

Utwórzmy podstawową aplikację Angular używając Angular CLI. W naszym folderze projektu uruchom swój ulubiony terminal i wykonaj następującą komendę:

ng new rxjs-chat

Daj jej trochę czasu. Po zakończeniu tworzenia podstawowej struktury i zainstalowaniu pakietów npm zmienimy nazwę nowo utworzonego folderu projektu Angular na client. Następnie w tym samym folderze głównym tworzymy folder server dla naszego serwera Socket.IO. Teraz mamy dwa foldery wewnątrz naszego folderu rxjs-chat:

  • klient – Angularowa aplikacja kliencka
  • serwer – wkrótce będzie to Node’owy serwer Socket.IO
Instalacja lokalnych pakietów

W folderze serwera wygenerujemy plik package.json wykonując następującą komendę:

npm init -y

Po tym zainstalujemy nasze zależności i zapiszemy je w pliku package.json:

npm install express socket.io --save

W tym samym folderze utworzymy plik index.js dla naszej aplikacji serwerowej. Zawartość pliku powinna być następująca:

Tworzymy instancję express i przechowujemy ją w zmiennej app. Następnie tworzymy serwer z modułem http. Następnie przekazujemy express do metody http.Server(). Express będzie służył jako handler dla żądań do naszego serwera. W zamian otrzymujemy instancję serwera, którą przechowujemy w zmiennej server.

W kolejnych dwóch liniach kodu wiążemy socket.IO z naszym http server:

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

let io = socketIO(server);

Potem nasłuchujemy na zdarzenie połączenia z socket.IO i logujemy się, gdy użytkownik nawiąże połączenie z serwerem. Na koniec uruchamiamy serwer i nasłuchujemy na danym porcie, w naszym przypadku jest to port 3000.

Właściwie uruchommy nasz serwer uruchamiając poniższe polecenie w folderze server:
node index.js
W konsekwencji powinniśmy otrzymać następujący komunikat: „started on port: 3000”.

Connecting to Socket.IO

Mamy serwer działający na porcie 3000. Teraz czas zobaczyć jak możemy połączyć się z serwerem z naszej Angularowej aplikacji. W tym celu będziemy musieli zainstalować bibliotekę kliencką Socket.IO. Uruchomimy następujące polecenie w naszym folderze klienta:

npm install socket.io-client --save

While we are at it lets run the Angular app via Angular CLI:

ng serve -o

Now we got our app running. Możemy zobaczyć, że nasza domyślna przeglądarka właśnie otworzyła nową kartę wskazującą na localhost. Dlatego właśnie użyliśmy argumentu -o do otwarcia przeglądarki.

Tworzenie usługi do komunikacji z serwerem

Nazwę ją chat.service.ts app.chat.service.ts i umieszczę ją wewnątrz folderu src. Usługa będzie na razie tak prosta, jak to tylko możliwe:

Dodanie usługi do dostawców modułu

Dodajemy również naszą usługę ChatService do listy dostawców wewnątrz modułu AppModule w pliku app.module.ts:

Wstrzyknięcie usługi do naszego komponentu

Na koniec, zaimportujemy i użyjemy usługi wewnątrz naszego app.component.ts:

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

I wstrzykniemy ChatService w naszym konstruktorze:

constructor(chatService: ChatService) { }

W rezultacie mamy następujący plik:

Jeśli wszystko poszło zgodnie z oczekiwaniami po zapisaniu zmian powinieneś zobaczyć komunikat 'user connected’ w terminalu, w którym uruchomiłeś node app:

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

Więc możemy powiedzieć, że w końcu udało nam się połączyć z naszym serwerem Socket IO. Teraz nadszedł czas, aby faktycznie wysłać jakieś dane za pomocą zdarzeń Socket IO.

Wysyłanie wiadomości do Socket.IO

Czas wzbogacić naszą jak dotąd prostą usługę ChatService o opcję wysyłania wiadomości do serwera:

Aby to zadziałało, musimy użyć sendMessage() z naszego AppComponent:

Na koniec, dajmy użytkownikowi proste wejście tekstowe i przycisk, aby mógł wysłać wiadomość:

Aby to wszystko działało musimy nasłuchiwać na serwerze na zdarzenie 'new-message’, które wyemitowaliśmy z ChatService:

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

Teraz nasz index.js wygląda tak:

Jeśli chcesz pobawić się kodem, możesz go znaleźć w moim repo na GitHubie – rxjs-chat.

Komunikowanie się z innymi użytkownikami

Aby nasza wiadomość została rozesłana do innych użytkowników, wszystko co musimy zrobić, to zmienić console.log(message) wewnątrz naszego wywołania zwrotnego zdarzenia 'new-message’ na io.emit(message):

A co robi io.emit()? W prostych słowach wysyła zdarzenie do wszystkich podłączonych do serwera.

Komunikujmy się z naszym serwerem poprzez ChatService. W tym celu dodamy do naszej usługi nową metodę – getMessages. Będzie ona zwracała Observable, którą utworzymy za pomocą metody Observable.create(). Za każdym razem, gdy socket otrzyma nową wiadomość, użyjemy metody observer.next() aby przekazać ją do obserwatorów.

Potem zasubskrybujemy tę Observable z naszego AppComponentu:

Utworzyliśmy nową messages array, w której będziemy przechowywać wiadomości otrzymane z serwera. Będziemy musieli pokazać te wiadomości w naszym szablonie. To jest idealny przypadek użycia dla ngFor:

.