Skip to main content

@rx-angular/cdk/notifications

npm rx-angular CI Coverage Status

A small typed convenience helper to handle contextual state.

@rx-angular/cdk/notifications is a small set of helpers designed to handle contextual states display.

Key features

  • ✅ Handle the display of the default, suspense, error and complete templates
  • ✅ Enrich notifications with an additional suspense type
  • ✅ Help to have lazy template creation

Demos:

Install

npm install --save @rx-angular/cdk
# or
yarn add @rx-angular/cdk

Documentation

Resources

Example applications: A demo application is available on GitHub.

Motivation

rx-angular_cdk_notifications__toNotifiaction_michael_hladky

When dealing with asynchronouse code we always have some contextual information given that directly relate to the output of an asynchronouse code.

The a good example is a [Promise](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise) used in a UI where you can list items and search them.

The following states can apply to this UI:

  • Initial loading of the list. (loading spinner)
  • Display of the data (The actual value is now given and displayed)
  • Error in the asynchronouse process (A error message is displayed)
  • Completion of the process (Communicates that the process is completed)
  • Subsquent search actions (loading spinner)
@Component({
selector: 'any-component',
template: `
<ng-container
*ngIf="count$ | async as count; else loadingOrErrorOrComplete"
>
<p *ngIf="count > 0 && count !== undefined; else empty">
Count: {{ count }}
</p>

<ng-template #empty> Negative Count </ng-template>
</ng-container>

<ng-template #loadingOrErrorOrComplete>
<ng-container [ngSwitch]="isErrorCompleteOrLoading$ | async">
<p *ngSwitchCase="-1">Error!</p>

<p *ngSwitchCase="1">Complete!</p>

<p *ngSwitchCase="0">Loading...</p>
</ng-container>
</ng-template>
`,
})
export class AnyComponent {
// ...
}

If we organize them visually 4 states, 3 of them contextual are given:

  • suspense (communicating progress)
  • update/next (the result it self, or parts of it)
  • error (communicating error)
  • complete (communicating complete)

For those states we use the term reactive context which includes the state and contextual state of and asynchronouse process.

rx-angular_cdk_notifications__reactive-context_michael_hladky

Whith this concept we can create helpers that support to implement the handling of reactive context in a more elegant way.

A good example is the rxLet directive:

@Component({
selector: 'any-component',
template: `
<p
*rxIf="
count$;
let count;
else: empty;
suspense: loading;
error: error;
complete: complete
"
>
Count: {{ count }}
</p>

<ng-template #empty> Negative Count </ng-template>

<ng-template #error> Error! </ng-template>

<ng-template #complete> Complete! </ng-template>

<ng-template #loading> Loading... </ng-template>
`,
})
export class AnyComponent {
// ...
}

The Benefits

  • ✅ A mental model for contextual state
  • ✅ Typed contextual state
  • ✅ RxJS materialize and notification extension
  • ✅ A mental model for contextual state

Setup

The notifications features can be used directly from the cdk package or indirectly through the template package. To do so, install the cdk package and, if needed, the packages depending on it:

  1. Install @rx-angular/cdk
npm i @rx-angular/cdk
// or
yarn add @rx-angular/cdk

Usage

The whole section is about extending the notification channels with a 4th state. The new type is called RxNotifications. In the following we will see a couple of helper functions that deal with that type.

For wrapping a value into a RxNotification we provide 3 helpers:

RxErrorNotification

const errorNotification: RxErrorNotification<any> = toRxErrorNotification();
const errorNotification: RxErrorNotification<any> = toRxErrorNotification(
new Error()
);
const errorNotification: RxErrorNotification<string> = toRxErrorNotification(
new Error(),
'lastValue'
);

toRxSuspenseNotification

const toRxSuspenseNotification: RxSuspenseNotification<any> =
toRxSuspenseNotification();
const toRxSuspenseNotification: RxSuspenseNotification<string> =
toRxSuspenseNotification('lastValue');

toRxCompleteNotification

const toRxCompleteNotification: RxCompleteNotification<any> =
toRxCompleteNotification();
const toRxCompleteNotification: RxCompleteNotification<string> =
toRxCompleteNotification('lastValue');

rxMaterialize

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