Skip to content

Instantly share code, notes, and snippets.

@glendaviesnz
Last active December 14, 2018 02:58
Show Gist options
  • Save glendaviesnz/68184096b03fa3c8c826676f6ad3fdbd to your computer and use it in GitHub Desktop.
Save glendaviesnz/68184096b03fa3c8c826676f6ad3fdbd to your computer and use it in GitHub Desktop.
NgRX State Machine - fluent component state builder
// Instead of this ....
const componentStates = {
name: 'userApproval',
states: {
[actionTypes.loadUnapprovedUsers]: {
[ComponentStates.Idle]: { to: ComponentStates.Processing, action: PassthroughAction }
},
[actionTypes.loadUnapprovedUsersSuccess]: {
[ComponentStates.Processing]: { to: ComponentStates.Idle, action: PassthroughAction }
},
[actionTypes.loadUnapprovedUsersError]: {
[ComponentStates.Processing]: { to: ComponentStates.Idle }
}
}
}
// With the fluent builder, component states can be specified like this ...
const componentStates = this._componentStateBuilder
.create('userApproval')
.forAction(actionTypes.loadUnapprovedUsers)
.fromState(ComponentStates.Idle)
.toState(ComponentStates.Processing
.passThrough()
.forAction(actionTypes.loadUnapprovedUsersSuccess)
.fromState(ComponentStates.Processing)
.toState(ComponentStates.Idle)
.passThrough()
.forAction(actionTypes.loadUnapprovedUsersError)
.fromState(ComponentStates.Processing)
.toState(ComponentStates.Idle)
.terminate()
// and the fluent builder code ...
import { forOwn } from 'lodash';
import { Injectable } from '@angular/core';
import { PassthroughAction } from '../../core/state/component-state.actions';
import { ComponentStates } from './component.states';
import { Guard } from '../../shared/guard';
export interface ComponentState {
id?: string | number;
name: string;
disableWhenProcessing: boolean;
showProgressBar: boolean;
states: { [action: string]: ActionState };
}
export interface ActionState {
[state: string]: Transition;
}
export interface Transition {
to?: ComponentStates;
action?: Function;
terminate?: boolean;
}
@Injectable()
export class ComponentStateBuilder {
private _componentStates: ComponentState;
private _currentAction: string;
private _currentFromState: ComponentStates;
public create(componentName: string) {
Guard.notNothing(componentName, 'componentName');
this._currentAction = undefined;
this._currentFromState = undefined;
this._componentStates = { name: componentName, disableWhenProcessing: false, showProgressBar: true, states: {} };
return this;
}
public withId(id: string | number) {
Guard.notNothing(id, 'id');
this._componentStates.id = id;
return this;
}
public disableWhenProcessing() {
this._componentStates.disableWhenProcessing = true;
return this;
}
public showProgressBar(showProgressbar: boolean) {
this._componentStates.showProgressBar = showProgressbar;
return this;
}
public forAction(actionType: string) {
Guard.notNothing(actionType, 'actionType');
this._componentStates.states[actionType] = {};
this._currentAction = actionType;
return this;
}
public fromState(fromState: ComponentStates) {
Guard.notNothing(fromState, 'fromState');
this._componentStates.states[this._currentAction][fromState] = {};
this._currentFromState = fromState;
return this;
}
public toState(toState: ComponentStates) {
Guard.notNothing(toState, 'toState');
this._componentStates.states[this._currentAction][this._currentFromState].to = toState;
return this;
}
public passThrough() {
this._componentStates.states[this._currentAction][this._currentFromState].action = PassthroughAction;
return this;
}
public terminate() {
this._componentStates.states[this._currentAction][this._currentFromState].terminate = true;
return this;
}
public transformTo(action: Function) {
Guard.notNothing(action, 'action');
this._componentStates.states[this._currentAction][this._currentFromState].action = action;
return this;
}
public build() {
this.validate(this._componentStates.states);
return this._componentStates;
}
public validate(states: any) {
forOwn(states, (value: any, key: string) => {
forOwn(value, (actionValue: any, actionKey: string) => {
if (!actionValue.action && !actionValue.terminate) {
throw new Error(`The component state change for ${key} is missing a passthrough, transform, or terminate flag`);
}
});
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment