Skip to main content

@rx-angular/cdk/notifications

@rx-angular/cdk/notifications models the full reactive context of an async source as a stream: rxMaterialize turns an Observable<T> into an Observable<RxNotification<T>>, where every emission carries the current phase (suspense, next, error, or complete) alongside the value.

This is the capability with no native equivalent: native @if / @switch / @defer plus signals can branch on the phases of a source you already hold, but they cannot produce a first-class stream of those phases for you to pipe, transform, and share. That materialized stream is what powers RxAngular's reactive-context directives.

For the mental model behind these types, see the concept The reactive context; this page also contributes to it.

Import

import { RxNotification, RxNotificationKind, toRxErrorNotification, toRxSuspenseNotification, toRxCompleteNotification, rxMaterialize } from '@rx-angular/cdk/notifications';

RxNotificationKind

A const enum naming the four phases of the reactive context:

const enum RxNotificationKind {
Suspense = 'suspense',
Next = 'next',
Error = 'error',
Complete = 'complete',
}

The string values map directly to the default template names (suspense, next, error, complete) used by the template-package directives.


RxNotification<T>

A discriminated union over kind:

type RxNotification<T> = RxNextNotification<T> | RxSuspenseNotification<T> | RxErrorNotification<T> | RxCompleteNotification<T>;

Every member carries:

FieldTypeDescription
valueTThe current value (may be the last-known value on non-next phases).
kindRxNotificationKindThe phase discriminant.
errorper member (see below)The error on the error phase; false otherwise. RxNextNotification: boolean; RxSuspenseNotification: false; RxErrorNotification: any; RxCompleteNotification: false.
completebooleantrue on the complete phase.
hasValueboolean@deprecated; check kind === RxNotificationKind.Next instead.

The individual interfaces are RxNextNotification<T>, RxSuspenseNotification<T>, RxErrorNotification<T>, and RxCompleteNotification<T>.


rxMaterialize

function rxMaterialize<T>(): OperatorFunction<T, RxNotification<T>>;

Materializes a source into a stream of RxNotification. Built on RxJS materialize(), it maps RxJS notification kinds (N/E/C) to RxNotificationKind names and logs any error (it does not swallow it).

Returns: an OperatorFunction<T, RxNotification<T>>.

import { interval, Observable } from 'rxjs';
import { rxMaterialize } from '@rx-angular/cdk/notifications';
import { RxNotification } from '@rx-angular/cdk/notifications';

const updates$: Observable<number> = interval(3000);
const materialized$: Observable<RxNotification<number>> = updates$.pipe(rxMaterialize());

Because the phases are now values in a stream, a template can branch on them with native control flow:

import { Component } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
import { rxMaterialize } from '@rx-angular/cdk/notifications';

@Component({
selector: 'app-updates',
template: `
@if (state(); as n) {
@switch (n.kind) {
@case ('suspense') {
<p>Loading…</p>
}
@case ('next') {
<p>Value: {{ n.value }}</p>
}
@case ('error') {
<p>Error!</p>
}
@case ('complete') {
<p>Complete!</p>
}
}
}
`,
})
export class UpdatesComponent {
private readonly updates$ = interval(3000).pipe(rxMaterialize());
readonly state = toSignal(this.updates$);
}

toRxErrorNotification

function toRxErrorNotification<T>(error?: any, value?: T): RxErrorNotification<T>;

Builds an error-phase notification. error defaults to true when omitted.

toRxSuspenseNotification

function toRxSuspenseNotification<T>(value?: T): RxSuspenseNotification<T>;

Builds a suspense-phase notification, the extra phase RxAngular adds beyond RxJS's next/error/complete to represent "in progress, nothing yet."

toRxCompleteNotification

function toRxCompleteNotification<T>(value?: T): RxCompleteNotification<T>;

Builds a complete-phase notification.


See also