JSPM

  • Created
  • Published
  • Downloads 1385
  • Score
    100M100P100Q109545F
  • License MIT

A set of extensions for Angular that enable writing highly reactive components using RxJS.

Package Exports

  • @lithiumjs/angular

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@lithiumjs/angular) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Lithium for Angular

A set of extensions for Angular that enable writing highly reactive components using RxJS.

Installation

The project can be installed via npm using the following command:

npm install @lithiumjs/angular

Quick Intro Guide

(For more information, see the full API reference)

Bootstrapping

Bootstrapping is required on the target class to enable event sources and state emitters for each instance. This is done via the Reactive class decorator.

Example

@Reactive()
@Component({...})
class Component {

    @OnInit() private onInit$: Observable<void> ;

    constructor () {
        this.onInit$.subscribe(() =>  console.log("Hello world."));
    }
}

API reference

EventSource

EventSource is the main decorator used for responding to events from a component. EventSource creates a proxy method for intercepting events (such as UI events or component lifecycle events) executed via callback, and translates them into observables.

Template

<button (click)="onButtonPress()"> </button> 

Component

@Reactive()
@Component({...})
class Component {

    @EventSource() private onButtonPress$: Observable<any> ;

    constructor () {
        this.onButtonPress$.subscribe(() =>  console.log("The button was pressed."));
    }
}

EventSource method decorators

Method decorators may be passed to EventSource and will be applied to the underlying facade method.

Example
@Reactive()
@Component({...})
class Component {

    @EventSource(HostListener("click")) private onClick$: Observable<any> ;

    constructor () {
        this.onClick$.subscribe(() =>  console.log("Component was clicked."));
    }
}

API reference

StateEmitter

StateEmitter is the decorator used to automatically synchronize state of a component, allowing for reactive communication to and from the UI via subjects.

Template

<div> You clicked the button {{buttonPressCount}} times.</div> 
<button (click)="onButtonPress()"> </button> 

Component

@Reactive()
@Component({...})
class Component {

    @EventSource() private onButtonPress$: Observable<any> ;

    @StateEmitter({initialValue: 0}) private buttonPressCount$: Subject<number> ;

    constructor () {
        this.onButtonPress$
            .flatMap(() =>  this.buttonPressCount$.take(1))
            .subscribe(buttonPressCount =>  this.buttonPressCount$.next(buttonPressCount + 1));
    }
}

StateEmitter property decorators

Property decorators may be passed to StateEmitter and will be applied to the underlying property.

Example
<component [disabled]="true"> </component> 
@Reactive()
@Component({
    selector: "component",
    ...
})
class Component {

    @StateEmitter(Input()) private disabled$: Subject<boolean> ;

    constructor () {
        this.disabled$.subscribe(disabled =>  console.log(`Disabled: ${disabled}`)); // Output: Disabled: true
    }
}

Proxied StateEmitters

Angular components will often use information from services and other sources. Proxied StateEmitters can be used to represent extenal values from within a component, or even create aliases for existing values.

StateEmitter proxies create reactive references to properties within the component class and allow them to be used like any other StateEmitter reference. Dot path notation may be used to reference nested properties.

Example

class FooService {

    public get nestedProperty(): number {
        return 42;
    }
}
@Reactive()
@Component({...})
class Component {

    @StateEmitter({
        proxyMode: EmitterMetadata.ProxyMode.Alias,
        proxyPath: "fooService.nestedProperty",
    })
    private nestedProperty$: Observable<number> ;

    constructor (private fooService: FooService) { }
}

Static vs dynamic proxy property paths

Proxy paths are considered either dynamic or static depending on the type of the properties within it. If a proxy path is dynamic, the resulting reference to the property will be an Observable. If a path is static, the reference will be a Subject.

A proxy path is considered static only if all of the following conditions are met:

  • The last property in the path is a Subject.
  • All other properties in the path are not of type Subject or Observable.
Example
interface Settings {

    notificationsEnabled: boolean;
    someOtherSetting: number;
}

class SettingsService {

    public settings$ = new BehaviorSubject<Settings> ({
        notificationsEnabled: true,
        someOtherSetting: 1
    });
}
@Reactive()
@Component({...})
class FormComponent {

    @StateEmitter.Alias("settingsService.settings$")
    private settings$: Subject<Settings> ;

    @StateEmitter.Alias("settings$.notificationsEnabled")
    private notificationsEnabled$: Observable<boolean> ;

    constructor (private settingsService: SettingsService) { }
}

In the above example, the proxy path settingsService.settings$ is considered static, because the last property is a Subject and the rest of the path does not contain any Observables or Subjects. The proxy path settings$.notificationsEnabled is not static, because the last property is not a Subject, and the first property in the path is a Subject.

Updating Subjects in dynamic proxy property paths

If a dynamic property path contains a Subject, it will automatically be notified of changes to the proxied property. The example below illustrates two-way binding of an aliased property with a dynamic property path containing a Subject.

Example
<form>
    <input [(ngModel)]="notificationsEnabled" type="checkbox">
</form>
@Reactive()
@Component({...})
class FormComponent {

    // Dynamic proxy property path that contains a Subject
    @StateEmitter.Alias("settingsService.settings$.notificationsEnabled")
    private notificationsEnabled$: Observable<boolean> ;

    constructor (private settingsService: SettingsService) { }
}

When notificationsEnabled is updated via the form input, settingsService.settings$ will automatically emit a merged Settings object with the new value of notificationsEnabled. All other property values on the Settings object will also be preserved.

Types of StateEmitter Proxies

  • Alias
  • From
  • Merge

Alias

The Alias proxy type simply resolves the given property path and creates an observable that directly maps back to the property value.

Example
class SessionManager {

    public session$: Subject<Session> ;
}
<div> Welcome back, {{session.username}}</div> 
@Reactive()
@Component({...})
class Component {

    @StateEmitter.Alias("sessionManager.session$") private session$: Subject<Session> ;

    constructor (private sessionManager: SessionManager) { }
}

Alias can also be used with other StateEmitter references:

<div> Welcome back, {{username}}</div> 
@Reactive()
@Component({...})
class Component {

    @StateEmitter.Alias("sessionManager.session$") private session$: Subject<Session> ;
    @StateEmitter.Alias("session$.username") private username$: Observable<string> ;

    constructor (private sessionManager: SessionManager) { }
}

From

The From proxy type creates a new Subject that gets its initial value from the source.

Example
<form>
    <input type="text" [(ngModel)]="username">
</form>
@Reactive()
@Component({...})
class FormComponent {

    @StateEmitter.From("sessionManager.session$.username") private username$: Subject<string> ;

    constructor (private sessionManager: SessionManager) { }
}

In the above example, any form updates to username will only be reflected on FormComponent.username$. sessionManager.session$ will not receive any updates.

Merge

The Merge proxy type creates a new Subject that subscribes to all updates from the source.

Example
<form>
    <input type="date" [(ngModel)]="date">
</form>
@Reactive()
@Component({...})
class FormComponent {

    @StateEmitter.Merge("fooService.date$") private date$: Subject<Date> ;

    constructor (private fooService: FooService) { }
}

In the above example, any form updates to date will only be reflected on FormComponent.date$. fooService.date$ will not receive any updates.

API reference

Lifecycle Event Decorators

Helper decorators are provided that proxy all of the Angular component lifecycle events. These are:

  • OnChanges
  • OnInit
  • OnDestroy
  • DoCheck
  • AfterContentInit
  • AfterContentChecked
  • AfterViewInit
  • AfterViewChecked

Example

@Reactive()
@Component({...})
class Component {

    @OnInit() private onInit$: Observable<void> ;

    constructor () {
        this.onInit$.subscribe(() =>  "Component is initialized.");
    }
}

API reference

API

Reactive

function Reactive(): ClassDecorator

A decorator that bootstraps the target class, which wires up all own and inherited EventSources and StateEmitters to each class instance.

EventSource

function EventSource(): EventSourceDecorator
function EventSource(...methodDecorators: MethodDecorator[]): EventSourceDecorator
function EventSource(eventType?: EventType, ...methodDecorators: MethodDecorator[]): EventSourceDecorator

Creates an event source, which is an Observable that automatically emits when the given function (eventType) is called.

eventType - The name of the function that represents the event or action.

methodDecorators - A list of MethodDecorators that should be applied to the underlying event function.

Note: If the target property's name is of the format "<eventType> $", eventType can be omitted and automatically deduced from the property name.

StateEmitter

function StateEmitter(): StateEmitterDecorator
function StateEmitter(...propertyDecorators: PropertyDecorator[]): StateEmitterDecorator
function StateEmitter(params: StateEmitter.DecoratorParams, ...propertyDecorators: PropertyDecorator[]): StateEmitterDecorator

Creates a state emitter, which is a Subject that automatically emits when the underlying property value is modified, and automatically updates the property value when the Subject emits.

params - The parameters to use for this state emitter. See StateEmitter.DecoratorParams.

propertyDecorators - A list of PropertyDecorators that should be applied to the underlying property.

Note: If the target property's name is of the format "<emitterType> $", params.propertyName can be omitted and automatically deduced from the property name.

StateEmitter.Alias

Helper decorator that creates a StateEmitter with proxyMode set to Alias.

function Alias(params: ProxyDecoratorParams | string, ...propertyDecorators: PropertyDecorator[]): PropertyDecorator

params - The parameters to use for this state emitter, or a string that is shorthand for passing the path parameter. See StateEmitter.ProxyDecoratorParams.

propertyDecorators - A list of PropertyDecorators that should be applied to the underlying property.

Note: This is functionally equivalent to:

StateEmitter({proxyMode: EmitterMetadata.ProxyMode.Alias});

StateEmitter.From

Helper decorator that creates a StateEmitter with proxyMode set to From.

function From(params: ProxyDecoratorParams | string, ...propertyDecorators: PropertyDecorator[]): PropertyDecorator

params - The parameters to use for this state emitter, or a string that is shorthand for passing the path parameter. See StateEmitter.ProxyDecoratorParams.

propertyDecorators - A list of PropertyDecorators that should be applied to the underlying property.

Note: This is functionally equivalent to:

StateEmitter({proxyMode: EmitterMetadata.ProxyMode.From});

StateEmitter.Merge

Helper decorator that creates a StateEmitter with proxyMode set to Merge.

function Merge(params: ProxyDecoratorParams | string, ...propertyDecorators: PropertyDecorator[]): PropertyDecorator

params - The parameters to use for this state emitter, or a string that is shorthand for passing the path parameter. See StateEmitter.ProxyDecoratorParams.

propertyDecorators - A list of PropertyDecorators that should be applied to the underlying property.

Note: This is functionally equivalent to:

StateEmitter({proxyMode: EmitterMetadata.ProxyMode.Merge});

StateEmitter.DecoratorParams

interface DecoratorParams {
    propertyName?: EmitterType;
    initialValue?: any;
    readOnly?: boolean;
    proxyMode?: ProxyMode;
    proxyPath?: string;
    proxyMergeUpdates?: boolean;
}

propertyName - (Optional) The name of the underlying property that should be created for use by the component's template. If not specified, the name will try to be deduced from the name of the StateEmitter property.

initialValue - (Optional) The initial value to be emitted. Defaults to undefined.

readOnly - (Optional) Whether or not the underlying property being created should be read-only. Defaults to false.

proxyMode - (Optional) The proxy mode to use for the StateEmitter. Defaults to None. For all possible values, see StateEmitter.ProxyMode.

proxyPath - (Conditional) The path of the property to be proxied. Required if proxyMode is not set to None.

proxyMergeUpdates - (Optional) Whether or not newly emitted values via dynamic proxy property paths should be merged with the previously emitted value. Defaults to true if the emitted value is an instance of Object, otherwise defaults to false.

StateEmitter.ProxyDecoratorParams

interface ProxyDecoratorParams {
    path: string;
    propertyName?: EmitterType;
    mergeUpdates?: boolean;
}

path - See StateEmitter.DecoratorParams.proxyPath.

propertyName - (Optional) See StateEmitter.DecoratorParams.propertyName.

mergeUpdates - (Optional) See StateEmitter.DecoratorParams.proxyMergeUpdates.

StateEmitter.EventType

type EventType = string;

StateEmitter.EmitterType

type EmitterType = string;

StateEmitter.ProxyMode

type ProxyMode = keyof {
    None,
    From,
    Alias,
    Merge
}

Angular Lifecycle EventSource decorators

OnChanges

function OnChanges(): EventSourceDecorator

OnInit

function OnInit(): EventSourceDecorator

OnDestroy

function OnDestroy(): EventSourceDecorator

DoCheck

function DoCheck(): EventSourceDecorator

AfterContentInit

function AfterContentInit(): EventSourceDecorator

AfterContentChecked

function AfterContentChecked(): EventSourceDecorator

AfterViewInit

function AfterViewInit(): EventSourceDecorator

AfterViewChecked

function AfterViewChecked(): EventSourceDecorator

Other information