Last active
December 14, 2018 02:58
-
-
Save glendaviesnz/68184096b03fa3c8c826676f6ad3fdbd to your computer and use it in GitHub Desktop.
NgRX State Machine - fluent component state builder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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