Learn how to connect and react to events from a WebSocket in your Angular application.
This material is part of the mobile development course for Media Engineering.
Recommended reading
Using WebSocket on the front-end is supported by most - if not all - browsers since before 2011, thanks to a native HTML5 API.
Since there is a native WebSocket API, you technically don't need to install any
npm
packages to use it in your app. Those packages simply provides more high level implementations.
In this subject, we will build a very minimal Angular Service to connect, listen and send messages to a backend offering WebSocket capabilities, based on the documentation for the WebSocket API.
WebSocket
The WebSocket
class is the main interface to the WebSocket API.
To connect to an existing WebSocket server, you simply need to instantiate a new WebSocket
object, providing the constructor with the WebSocket URL:
const ws = new WebSocket('ws://echo.websocket.org');
A WebSocket
object can be in one of four states, defined as static constants of the WebSocket
class:
CONNECTING
- The connection is not yet openedOPEN
- The connection is opened and messages can be exchangedCLOSING
- The connection is being closedCLOSED
- The connection is closed and messages can no longer be exchangedTo check the state in which a WebSocket
instance currently is, use its readyState
property:
if (ws.readyState === WebSocket.OPEN) { // Do something only if the connection is open}
We can create a new Angular service that will manage this WebSocket
object and proxy it to the components in which it is injected.
Create a new file, e.g. websocket.service.ts
with the following code:
import { Injectable } from '@angular/core';import { Observable, Observer, ReplaySubject } from 'rxjs';import { map, switchMap } from 'rxjs/operators';const WS_SERVER_URL = 'ws://echo.websocket.org';@Injectable({ providedIn: 'root' })export class WebsocketService { // A ReplaySubject will emit its X latest values (1 in this case) each time // its 'subscribe()' method is called private ws$ = new ReplaySubject<WebSocket>(1); constructor() { const socket = new WebSocket(WS_SERVER_URL); socket.onopen = () => { console.log('Successfully connected to the WebSocket at', WS_SERVER_URL); // When the connection is done, emit the WebSocket instance this.ws$.next(socket); }; }}
To use the service, simply inject it in one of your components:
// Other imports...import { WebsocketService } from 'path/to/websocket.service';@Component({ /* ... */ })export class ExampleComponent { // ... constructor(private wsService: WebsocketService) { // ... } // ...}
Injecting the service is enough to connect to the WebSocket, since we do this in the Service constructor.
Several of a WebSocket
object's properties can be set to react to specific events such as:
onopen
)onmessage
)onerror
)onclose
)Each of these porperties expect a callback function that will be called when the corresponding event occurs:
We already did this in the Angular Service to react to a successfull connection
// Log all messagesws.onmessage = message => console.log(message);
WebSocket > Listening to messages
As said in previous subjects, Angular 2+ heavily uses rxjs
' Observable
, which are very well suited to handle WebSocket messages.
Remember that Observable
allow us to subscribe to a particular stream of events in order to do something each time on of those events is fired, until the stream is closed.
From the the front-end point of view, a WebSocket connection is pretty much the same thing: a stream of messages emitted until the connection is closed.
It would thus be quite natural that our Angular WebSocketService
exposes an Observable
that emits a new value each time a message is broadcasted on the WebSocket connection.
The WebSocket API define such a message with the
MessageEvent
interface.
Let's create a new listen()
method on our WebSocketService
that returns an Observable<any>
(where any is the data of the message).
listen()
methodWebSocket > Listening to messages > Add listening capabilities to the Angular Service
Add this method to the service in websocket.service.ts
:
import { Injectable } from '@angular/core';import { Observable, Observer, ReplaySubject } from 'rxjs';import { map, switchMap } from 'rxjs/operators';@Injectable({ providedIn: 'root' })export class WebsocketService { // ... public listen<T = any>(): Observable<T> { // Only listen when the connection is opened return this.ws$.pipe( // Make an observable out of the websocket stream switchMap(socket => new Observable((subscriber: Observer<MessageEvent<T>>) => { // When a new message is received, the Observable will emit this message socket.onmessage = message => subscriber.next(message); // When a websocket error occurs, the Observable will emit a new error socket.onerror = error => subscriber.error(error); // When the websocket closes, the observable completes socket.onclose = () => subscriber.complete(); // Function that will be called if the user manually unsubscribe return () => socket.close(); }) ), // When a message is emitted, change the value to the message content map((event: MessageEvent<T>) => event.data) ); }}
WebSocket > Listening to messages > Add listening capabilities to the Angular Service
To listen to messages on the WebSocket
instance in your component, subscribe to its listen()
method:
// Imports...@Component({ /* ... */ })export class ExampleComponent { // ... constructor(private wsService: WebsocketService) { // ... this.wsService .listen() .subscribe(message => { // Do something when a message is received }); // ... } // ...}
WebSocket > Listening to messages > Add listening capabilities to the Angular Service
By default, the data that you can access in the listen()
subscribe callback is type with any
, since the method does not know the structure of the received message data. You can tell what type of data you expect to receive to the listen()
to have proper typings.
Let's suppose we designed our WebSocket API so that all our messages have a type
and content
property. We could define it in our app:
export type WsMessage = { type: string; content: any;};
Now, when calling the listen()
method, we can tell it that we expect to receive this type of data in our messages:
this.wsService.listen<WsMessage>().subscribe(message => { // message is now of type WsMessage.});
WebSocket > Listening to messages
Sending a message is very easy, once connected to a WebSocket.
Simply call the send()
method of a WebSocket
instance, passing it the data you want to send (usually some JSON
formatted value):
const data = { foo: 'bar'};ws.send(JSON.stringify(data));
Note that you could also send binary data (e.g. files) through this method.
WebSocket > Listening to messages > Send messages
To make our WebSocketService
capable of sending messages, we will create a new send()
method that takes an argument, and calls the WebSocket.send()
method.
// Importsconst WS_SERVER_URL = 'ws://echo.websocket.org';@Injectable({ providedIn: 'root' })export class WebsocketService { private ws: WebSocket; // ... public send(data: unknown): void { this.ws$.subscribe(socket => { socket.send(JSON.stringify(data)); }); }}
Note that the
data
param is typed asunknown
, because thesend()
method doesn't care (nor know) what type of data you want to send.
WebSocket > Listening to messages > Send messages
To send some message from the component, simply call our WebSocketService
's send()
method with a serializable value, e.g. a binded input value:
<input type="text" name="msg" id="msg" [(ngModel)]="message" /><button (click)="sendMessage()" >Envoyer</button>
// Imports...@Component({ /* ... */ })export class ExampleComponent { message: string; // ... sendMessage() { this.wsService.send({ msg: this.message }); } // ...}
As annouced, we've built a very simple Angular Service to manage websocket connection, and sending/receiving messages.
Some enhancment could be implemented, depending on your needs, such as:
send()
method,The complete Angular
WebSocketService
can be found here
Using WebSocket on the front-end is supported by most - if not all - browsers since before 2011, thanks to a native HTML5 API.
Since there is a native WebSocket API, you technically don't need to install any
npm
packages to use it in your app. Those packages simply provides more high level implementations.
In this subject, we will build a very minimal Angular Service to connect, listen and send messages to a backend offering WebSocket capabilities, based on the documentation for the WebSocket API.
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |