
In this article I will try to cover in detail the narrow scope of application of technology within the framework of Angular framework and its already integral assistant - RxJs, while we deliberately will not concern server implementations, since This is a complete topic for a separate article.
This text will be useful to those who are already familiar with Angular, but want to deepen their knowledge directly on the topic.
First, some basic information.
What is WebSocket and why do you need it
According to Wikipedia , WebSocket is a “full-duplex communication protocol (can transmit and receive simultaneously) over TCP connections, designed to exchange messages between a browser and a web server in real time.
WebSocket is designed to be implemented in web browsers and web servers, but it can be used for any client or server application. The WebSocket protocol is an independent protocol based on the TCP protocol. It enables closer interaction between the browser and the website, facilitating the distribution of interactive content and the creation of real-time applications. ”
In other words, WebSocket allows the server to receive requests from the client and send requests to the client at any desired time, so the browser (client) and the server get equal rights and the ability to exchange messages when connected. A regular AJAX request requires the transfer of full HTTP headers, which means an increase in traffic in both directions, while the overhead of web sockets after the connection is established is only two bytes. A web socket reduces the amount of transmitted information in HTTP headers hundreds and thousands of times and reduces the waiting time. Web sockets connections support cross-domain, like CORS.
On the server side there are their own packages for supporting a web socket, on the client this is the HTML5 WebSocket API, which has an interface of three methods:
WebSocket - the main interface for connecting to the WebSocket server, and then sending and receiving data over the connection;
CloseEvent - the event dispatched by the WebSocket object when closing the connection;
MessageEvent is an event sent by a WebSocket object when a message is received from a server.
This is how it looks at the implementation level of javascript:
const ws = new WebSocket("ws://www.example.com/socketserver", "protocolOne"); ws.onopen = () => { ws.onmessage = (event) => { console.log(event); } ws.send("Here's some text that the server is urgently awaiting!"); };
onmessage - we listen to messages from the server
send - we
send our messages to the server
That is, in the base view, everything is extremely simple, but if you have a desire to delve into the topic, you can refer to
MDN Web Docs and at the same time study libraries that implement their own layers on top of this API.
Why you shouldn’t be afraid to use WebSocket
The first thing that can scare away is
browser support . To date, there is no such problem - WebSocket is supported almost completely both on the web and in the mobile segment.

https://caniuse.com/#feat=websocketsThe second point is the
ease of implementation . Yes, at first this discourages.
The API is so simple that at first glance it can be hard to understand how to work with such a modest number of methods, because all of them, except for one, either report errors or connection, and only one of them,
onmessage , carries the for which websockets are used, i.e. for receiving data from the server.
In this case, the snag is that the server usually sends different data, therefore, we need several different onmessage? Or do you need to create your own connection for each data model?
So, the task: you need to take from the server the user model and the latest news model, and maybe even something else.
I have come across such an "elegant" implementation:
const wsUser = new WebSocket("ws://www.example.com/user"); wsUser.onmessage = (event) => { // ... }; const wsNews = new WebSocket("ws://www.example.com/news"); wsNews.onmessage = (event) => { // ... }; const wsTime = new WebSocket("ws://www.example.com/time"); wsTime.onmessage = (event) => { // ... }; const wsDinner = new WebSocket("ws://www.example.com/dinner"); wsDinner.onmessage = (event) => { // ... }; const wsCurrency = new WebSocket("ws://www.example.com/currency"); wsCurrency.onmessage = (event) => { // ... }; const wsOnline = new WebSocket("ws://www.example.com/online"); wsOnline.onmessage = (event) => { // ... }; const wsLogin = new WebSocket("ws://www.example.com/login"); wsLogin.onmessage = (event) => { // ... }; const wsLogout = new WebSocket("ws://www.example.com/logout"); wsLogout.onmessage = (event) => { // ... };
At first glance, everything is logical. But now imagine what it would look like if there are tens or hundreds of them. On one of the projects I had to work on, there were about three hundred events.

Solve the problem.
All third-party libraries for working with webboxes allow you to subscribe to messages by the type addEventListener. It looks something like this:
ws.on("user", (userData) => { / .. })
As we know, we can operate with a single method -
onmessage , which gets all the data within its connection, so this code looks somewhat unusual. This is implemented as follows:
onmessage returns a
MessageEvent that contains a
data field. It is
data that contains the information that the server sends us. This object looks something like this:
{ "event": "user", "data": { "name": "John Doe", ... } }
where
event is the key by which you can determine exactly what information the server sent. Then a bus is created on the frontend side, which filters information by event and sends it to the right address:
const ws = new WebSocket("ws://www.example.com"); ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.event === 'user') {
This provides the ability to receive different data within a single connection and subscribe to it through syntax similar to JS events.
WebSockets in Angular
Finally, we got to the most important thing - using WebSockets directly in Angular.
Despite the simplicity of working with the native WebSocket API, in this article we will use RxJs, which of course, we are talking about Angular.
The native WebSocket API can be used in applications on Angular, create an easy-to-use interface, RxJs Observable, subscribe to the necessary messages, etc., but RxJs has already done the main work for you: WebSocketSubject is a reactive wrapper over the standard WebSocket API. It does not create an event bus or reconnect processing. This is a regular Subject, with which you can work with reactive-style web sockets.
RxJs WebSocketSubject
So,
WebSocketSubject expects a
WebSocketSubjectConfig and an optional destination, in which you can send a link to your observable Subject, creates an Observable, through which you can listen and send messages for websockets.
Simply put, you pass the url of the connection as an argument to the WebSocketSubject and subscribe in the usual way for RxJs to the entire activity of the web socket. And if you need to send a message to the server, then use the same usual method webSocketSubject.next (data).
We do service for work with WebSocket Angular
Briefly describe what we expect from the service:
- Single and concise interface;
- Ability to configure the connection level dependencies DI ;
- Possibility of reuse;
- Typification;
- The ability to subscribe to receive information on the key;
- Ability to terminate the subscription;
- Sending messages to the server;
- Reconnect
At the last point should pay special attention. Reconnect, or the organization of reconnection to the server, is a primary factor when working with websockets, since Network breaks, server crashes, or other errors that cause a disconnection can cause the application to crash.
It is important to note that reconnection attempts
should not be too frequent and should not continue indefinitely, since this behavior is capable of hanging the client.
Let's get started
First of all, we will create a service config interface and a module that will provide the ability to be configured when connected.
If possible, I will shorten the code; you can see the full version
in the Angular Russian community on GitHub .
export interface WebSocketConfig { url: string; reconnectInterval?: number; reconnectAttempts?: number; } export class WebsocketModule { public static config(wsConfig: WebSocketConfig): ModuleWithProviders { return { ngModule: WebsocketModule, providers: [{ provide: config, useValue: wsConfig }] }; } }
Next, we need to describe the interface of the webbox message:
export interface IWsMessage<T> { event: string; data: T; }
where
event is the key, and
data received by the key is the typed model.
The public interface of the service looks like this:
export interface IWebsocketService { on<T>(event: string): Observable<T>; send(event: string, data: any): void; status: Observable<boolean>; }
Service has fields:
// WebSocketSubject private config: WebSocketSubjectConfig<IWsMessage<any>>; private websocketSub: SubscriptionLike; private statusSub: SubscriptionLike; // Observable interval private reconnection$: Observable<number>; private websocket$: WebSocketSubject<IWsMessage<any>>; // , private connection$: Observer<boolean>; // Observable private wsMessages$: Subject<IWsMessage<any>>; // private reconnectInterval: number; // private reconnectAttempts: number; // private isConnected: boolean; // public status: Observable<boolean>;
In the constructor of the service class, we get the WebSocketConfig object specified when the module is connected:
constructor(@Inject(config) private wsConfig: WebSocketConfig) { this.wsMessages$ = new Subject<IWsMessage<any>>(); // , , this.reconnectInterval = wsConfig.reconnectInterval || 5000; this.reconnectAttempts = wsConfig.reconnectAttempts || 10; // connection$ websocket$ this.config = { url: wsConfig.url, closeObserver: { next: (event: CloseEvent) => { this.websocket$ = null; this.connection$.next(false); } }, // connection$ openObserver: { next: (event: Event) => { console.log('WebSocket connected!'); this.connection$.next(true); } } }; // connection status this.status = new Observable<boolean>((observer) => { this.connection$ = observer; }).pipe(share(), distinctUntilChanged()); // this.statusSub = this.status .subscribe((isConnected) => { this.isConnected = isConnected; if (!this.reconnection$ && typeof(isConnected) === 'boolean' && !isConnected) { this.reconnect(); } }); // , - this.websocketSub = this.wsMessages$.subscribe( null, (error: ErrorEvent) => console.error('WebSocket error!', error) ); // this.connect(); }
The connection method itself is simple:
private connect(): void { this.websocket$ = new WebSocketSubject(this.config); // // , , // , // , this.websocket$.subscribe( (message) => this.wsMessages$.next(message), (error: Event) => { if (!this.websocket$) { // run reconnect if errors this.reconnect(); } }); }
Reconnect is a bit more complicated:
private reconnect(): void { // interval reconnectInterval this.reconnection$ = interval(this.reconnectInterval) .pipe(takeWhile((v, index) => index < this.reconnectAttempts && !this.websocket$)); // , this.reconnection$.subscribe( () => this.connect(), null, () => { // Subject complete if reconnect attemts ending this.reconnection$ = null; if (!this.websocket$) { this.wsMessages$.complete(); this.connection$.complete(); } }); }
The
on method is also extremely simple, there is nothing to even comment on.
public on<T>(event: string): Observable<T> { if (event) { return this.wsMessages$.pipe( filter((message: IWsMessage<T>) => message.event === event), map((message: IWsMessage<T>) => message.data) ); } }
The
send method is even simpler:
public send(event: string, data: any = {}): void { if (event && this.isConnected) { // any , "" string // :) this.websocket$.next(<any>JSON.stringify({ event, data })); } else { console.error('Send error!'); } }
That's the whole service. As you can see, the bulk of the code fell on the organization of the reconnect.
Let's now see how to use it. Let's connect the WebsocketModule module:
imports: [ WebsocketModule.config({ url: environment.ws // 'ws://www.example.com' }) ]
In the component constructor, we will inject the service and subscribe to messages from '
messages ', send back the text to the server:
constructor(private wsService: WebsocketService) { this.wsService.on<IMessage[]>('messages') .subscribe((messages: IMessage[]) => { console.log(messages); this.wsService.send('text', 'Test Text!'); }); }
The name of the event is more convenient to make in a constant or enumeration. Create somewhere websocket.events.ts file and write to it:
export const WS = { ON: { MESSAGES: 'messages' }, SEND: { TEXT: 'text' } };
Rewrite subscriptions using the created WS object:
this.wsService.on<IMessage[]>(WS.ON.MESSAGES) .subscribe((messages: IMessage[]) => { console.log(messages); this.wsService.send(WS.SEND.TEXT, 'Test Text!'); });

In conclusion
That's all. This is a necessary minimum that the Angular developer needs to know about WebSockets. I hope that I have covered this topic quite clearly. The full version of the service can be found on
GitHub .
For all questions, please contact the comments,
to me in Telegrams or on
the Angular channel in the same place.