Implementing Real-Time Charts with WebSockets
Environment
| Product | Progress® Kendo UI® for Angular Charts |
Description
How can I display real-time data which comes from a WebSocket server in the Kendo UI for Angular Chart?
Solution
Apart from displaying real-time data which comes from a WebSocket, the Chart provides options for setting a bidirectional (full-duplex) type of communication and allows multiple clients to push messages to the server, which will be received from all connected clients.
Configuring the Server
The sample project features a Node.js Express server, which serves the Angular application and provides the data feed for the Chart through a WebSocket stream.
The following example demonstrates how to set the HTTP server.
app.use(express.static(path.join(__dirname, '../../dist/websocket-charts')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../../dist/websocket-charts/index.html'));
});
const port = process.env.PORT || 8000;
const server = http.createServer(app);
server.listen(port, () => {
console.log(`HTTP server running on port ${port}...`);
});
The following example demonstrates how to set the WebSocket server.
const wsPort = 8088;
const wsServer = new ws.Server({port: wsPort});
console.log(`WebSocket server is running on port ${wsPort}...`);
wsServer.on('connection',
websocket => {
setInterval(() => {
// Broadcasting to all clients
wsServer.clients.forEach(
client => client.send(JSON.stringify({
value: Math.random() * 50,
time: new Date()
})));
}, 1000);
// Broadcasting messages from any client to all clients
websocket.on('message', message => {
const now = new Date();
wsServer.clients.forEach(
client => client.send(JSON.stringify({
text: message,
time: now })));
});
});
Configuring the Client
The following example demonstrates how to configure the WebSocket service.
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class WebSocketService {
public ws: WebSocket;
public createObservableSocket(url: string): Observable<string> {
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = event => observer.next(event.data);
this.ws.onerror = event => observer.error(event);
this.ws.onclose = event => observer.complete();
// Return teardown function to close the WebSocket when unsubscribing
return () => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.close();
}
};
});
}
public sendMessage(message: string): void {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(message);
}
}
}
Configuring the Chart
The following example demonstrates how to configure the Chart.
<kendo-chart [transitions]="false">
<kendo-chart-series>
<kendo-chart-title text="Current temp (°C)"></kendo-chart-title>
<kendo-chart-series-item
type="scatterLine"
[data]="chartValues"
yField="value" xField="time">
</kendo-chart-series-item>
</kendo-chart-series>
<kendo-chart-x-axis>
<kendo-chart-x-axis-item
baseUnit="seconds"
baseUnitSteps="seconds"
[min]="min"
[max]="max">
</kendo-chart-x-axis-item>
</kendo-chart-x-axis>
</kendo-chart>
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
export class AppComponent implements OnDestroy {
public chartValues: Array<{ value: number; time: Date }> = [];
public messages: Array<{ text: string; time: Date }> = [];
public min: Date = new Date();
public max: Date = new Date(this.min.getTime() + 20000);
private url = 'ws://localhost:8088';
private subscription: Subscription;
constructor(private wsService: WebSocketService) {
this.subscription = wsService.createObservableSocket(this.url)
.subscribe(m => {
const item = JSON.parse(m);
item.time = new Date(item.time);
if (item.value) {
this.chartValues = [...this.chartValues, item];
if (this.chartValues.length > 20) {
this.min = this.chartValues[this.chartValues.length - 20].time;
this.max = item.time;
}
} else {
this.messages = [...this.messages, item];
}
});
}
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}