Real-Time Communication
AngularTS provides five building blocks for real-time and compute-heavy work:
$ssefor Server-Sent Events.$websocketfor bidirectional WebSocket connections.$webTransportfor HTTP/3 WebTransport sessions with datagrams and streams.ng-workerfor JavaScript work in a Web Worker.ng-wasmfor loading WebAssembly modules.
Exact service and connection signatures live in TypeDoc:
SseServiceSseConfigSseConnectionWebSocketServiceWebSocketConfigWebSocketConnectionWebTransportServiceWebTransportConfigWebTransportConnectionWorkerConfigWorkerHandleWorkerError
Server-Sent Events
$sse creates a managed EventSource connection. It handles query parameters, JSON message parsing, heartbeat detection, automatic reconnection, and clean shutdown.
class NewsFeedController {
static $inject = ["$sse", "$scope"];
items: NewsItem[] = [];
private connection: ng.SseConnection;
constructor($sse: ng.SseService, $scope: ng.Scope) {
this.connection = $sse("/api/news/stream", {
withCredentials: true,
params: { category: "top" },
retryDelay: 3000,
heartbeatTimeout: 30000,
onMessage: (item: NewsItem) => {
const view = $scope["$ctrl"] as NewsFeedController;
view.items = [item, ...view.items].slice(0, 50);
},
onReconnect: (attempt) => {
console.log("SSE reconnect", attempt);
},
});
$scope.on("$destroy", () => this.connection.close());
}
}
EventSource does not support arbitrary custom headers in browsers. For authenticated streams, use cookies with withCredentials or include a short-lived token in query params.
WebSockets
$websocket returns a managed WebSocket connection with reconnects, heartbeat handling, message transforms, and send support.
class ChatController {
static $inject = ["$websocket", "$scope"];
messages: ChatMessage[] = [];
private socket: ng.WebSocketConnection;
constructor($websocket: ng.WebSocketService, $scope: ng.Scope) {
this.socket = $websocket("wss://api.example.com/chat", {
protocols: ["v1"],
retryDelay: 2000,
maxRetries: 20,
onMessage: (message: ChatMessage) => {
const view = $scope["$ctrl"] as ChatController;
view.messages = [...view.messages, message];
},
onClose: (event) => {
console.log("WebSocket closed", event.code);
},
});
$scope.on("$destroy", () => this.socket.close());
}
send(text: string) {
this.socket.send({ type: "message", text });
}
}
send() serializes values as JSON before passing them to the native WebSocket.
WebTransport
$webTransport opens a browser-native WebTransport session. Use it when an
endpoint can serve HTTP/3 and the client benefits from unreliable datagrams,
reliable streams, or both in the same session.
class TelemetryController {
static $inject = ["$webTransport", "$scope"];
events: string[] = [];
private session: ng.WebTransportConnection;
constructor($webTransport: ng.WebTransportService, $scope: ng.Scope) {
this.session = $webTransport("https://localhost:4433/webtransport", {
reconnect: true,
retryDelay: 500,
maxRetries: 5,
requireUnreliable: true,
transformDatagram: (data) => new TextDecoder().decode(data),
onDatagram: ({ message }) => {
const view = $scope["$ctrl"] as TelemetryController;
view.events = [...view.events, String(message)];
},
onReconnect: ({ connection }) => {
return connection.sendText(JSON.stringify({ subscribe: "telemetry" }));
},
});
}
send(value: string) {
return this.session.sendText(value);
}
}
The service expects the browser WebTransport API to exist and requires an
https: URL with an explicit port. The test backend exposes certificate hash
metadata at /webtransport/cert-hash for local browser tests.
Reconnect is opt-in at the service layer. When enabled, the
WebTransportConnection object stays stable while its native transport
instance is replaced. Use onReconnect as the renegotiation hook for
subscriptions, authentication messages, or other session state that the server
does not remember across HTTP/3 sessions.
For template-level feeds, ng-web-transport connects on load by default and
evaluates lifecycle expressions. data-mode="datagram" is the default;
data-mode="stream" reads server-opened unidirectional streams.
<div
ng-web-transport="transportUrl"
data-config="transportConfig"
data-mode="datagram"
data-transform="json"
data-as="session"
data-reconnect="true"
on-message="events.push($message)"
on-reconnect="reconnects = $attempt"
on-error="error = error"
></div>
data-transform accepts bytes, text, or json. Message expressions receive
$connection, $data, $message, $event, and $text for text/json modes.
Reconnect is opt-in with data-reconnect="true"; tune it with
data-retry-delay and data-max-retries. on-reconnect runs after the
replacement session is ready and receives $attempt, $connection, error,
and $url.
Web Workers
Use ng-worker when a view action should run CPU-heavy JavaScript outside the main thread.
<button
ng-worker="./workers/compress.js"
data-params="vm.fileBuffer"
on-result="vm.compressed = $result"
on-error="vm.error = error"
trigger="click"
>
Compress
</button>
<span
ng-worker="./workers/sensor-reader.js"
interval="5000"
on-result="vm.sensorData = $result"
></span>
The directive evaluates data-params, posts the result to the worker, and
exposes worker responses as $result in on-result. Worker output is never
interpreted as HTML. Bind it into scope or a model and let normal directives
render the result.
Register expensive or shared workers once and reference their injectable
handle. data-request enables correlated request-response delivery:
app.worker("compressWorker", "./workers/compress.js", {
restart: true,
maxRestarts: 3,
});
<button
ng-worker
data-handle="compressWorker"
data-request
data-params="vm.fileBuffer"
on-result="vm.compressed = $result"
on-error="vm.error = error"
>
Compress
</button>
The directive subscribes to a shared handle but does not terminate it when the
element scope is destroyed. Workers created directly from an ng-worker script
path remain element-owned and are terminated with that scope.
A worker module receives values through self.onmessage and returns results through self.postMessage.
self.onmessage = function ({ data: { limit } }) {
const sieve = new Uint8Array(limit + 1).fill(1);
sieve[0] = sieve[1] = 0;
for (let i = 2; i * i <= limit; i++) {
if (sieve[i]) {
for (let j = i * i; j <= limit; j += i) sieve[j] = 0;
}
}
const primes = [];
for (let i = 2; i <= limit; i++) {
if (sieve[i]) primes.push(i);
}
self.postMessage(primes);
};
WebAssembly
ng-wasm loads a .wasm file and exposes its WasmResource on the scope under
a configurable name. Await resource.ready before reading
resource.exports.
<div
ng-wasm
src="/wasm/image-processor.wasm"
as="imageProcessor"
></div>
const resource = $scope.imageProcessor;
await resource.ready;
const result = resource.exports.grayscale(pixelBuffer, width, height);
WebAssembly loading is asynchronous. Use the resource status in templates and
await ready before calling exports from application code.
Choosing A Transport
| Need | Use |
|---|---|
| Server pushes one-way updates | $sse |
| Client and server both send messages | $websocket |
| HTTP/3 datagrams or streams | $webTransport |
| CPU-heavy JavaScript | ng-worker |
| Compiled compute module | ng-wasm |
Related
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.