@rx-angular/cdk/notifications
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
When dealing with asynchronous code we always have some contextual information given that directly relate to the output of an asynchronous code.
The good example is a 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 asynchronous process (A error message is displayed)
- Completion of the process (Communicates that the process is completed)
- Subsequent 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 asynchronous process.
With 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:
- 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());