npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ngx-fsm-rx

v1.0.5

Published

Angular wrapper around FsmRx, a Finite State Machine built upon RxJS and Typescript.

Downloads

11

Readme

NgxFsmRx

About

Angular wrapper around FsmRx, a Finite State Machine built upon RxJS and Typescript.

What is FsmRx

FsmRx allows developers to organise code into discrete states, each with their own strongly typed dataset. Transitions between these states are governed by an allowlist giving developers more control and visibility over program flow. Transitions also support the lifecycle hooks onLeave, onEnter and onUpdate. These callbacks are supplied data scoped to the relevant transition states and can be used for the implementation of any required build-up and teardown of state-specific interactions, logic, or calls.

FsmRx greatly reduces code complexity, speeding up development time while resulting in easier-to-maintain bug-free code.

Please visit here for a deep dive into all FsmRx has to offer.

Related projects

Further Documentation

For full documentation see:

Installation

ng add ngx-fsm-rx

Quick Start Guide - Schematics

Component

ng generate ngx-fsm-rx:component

Gif showing ng generate schematic for NgxFsmRx components

Service

ng generate ngx-fsm-rx:service

Quick Start Guide - Manual

Create FSM states union

type States = "foo" | "bar" | "baz";

Create FSM state data union

interface CommonData extends BaseStateData<States> {
    commonProperty: string;
}

interface FooData extends CommonData {
    state: "foo";
    fooProperty: number;
}

interface BarData extends CommonData {
    state: "bar";
    barProperty: string;
}

interface BazData extends CommonData {
    state: "baz";
    bazProperty: boolean;
}

type StateData = FooData | BarData | BazData;

Create CanLeaveToMap

As Type

type CanLeaveToMap = {
    FSMInit: "foo",
    foo: "bar",
    bar: "foo" | "baz";
    baz: "FSMTerminate";
};

As Interface

interface CanLeaveToMap extends CanLeaveToStatesMap<States> {
    FSMInit: "foo",
    foo: "bar",
    bar: "foo" | "baz";
    baz: "FSMTerminate";
}

Extend FsmRx and Define StateMap

Component

@Component({
    selector: 'app-foo-bar-baz-fsm',
    templateUrl: './foo-bar-baz-fsm.component.html',
    standalone: true,
    imports: [CommonModule],
    styleUrls: ['./foo-bar-baz-fsm.component.scss']
})
export class FooBarBazFSM  extends FsmRxComponent<States, StateData, CanLeaveToMap>{
     public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
        foo: {
            canEnterFromStates: { FSMInit: true, bar: true },
            canLeaveToStates: { bar: true }
        },
        bar: {
            canEnterFromStates: { foo: true },
            canLeaveToStates: { foo: true, baz: true }
        },
        baz: {
            canEnterFromStates: { bar: true },
            canLeaveToStates: { FSMTerminate: true }
        }
    };
}

Service

@Injectable({
  providedIn: 'root'
})
export class FooBarBazFSM  extends FsmRxInjectable<States, StateData, CanLeaveToMap>{
     public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
        foo: {
            canEnterFromStates: { FSMInit: true, bar: true },
            canLeaveToStates: { bar: true }
        },
        bar: {
            canEnterFromStates: { foo: true },
            canLeaveToStates: { foo: true, baz: true }
        },
        baz: {
            canEnterFromStates: { bar: true },
            canLeaveToStates: { FSMTerminate: true }
        }
    };
}

Define The Constructor and Transition to the First State

 public constructor() {
    super();
    this.changeState<"FSMInit">({ state: "foo", commonProperty: "some-string", fooProperty: 5 });     
}

Define onEnter, onLeave and onUpdate callbacks

Inline function

public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
    foo:{
        ...
        onEnter: (changes:OnEnterStateChanges<States, "foo", StateData, CanLeaveToMap>) => {
            // State buildup logic goes here 
        },
        onLeave: (changes:OnLeaveStateChanges<States, "foo", StateData, CanLeaveToMap>) => {
            // State teardown logic goes here 
        },
        onUpdate: (changes:OnUpdateStateChanges<States, "foo", StateData, CanLeaveToMap>) => {
            // State update logic goes here 
        }
    }
    ...
}

Regular function

public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
    foo:{
        ...
        onEnter: this.handleOnEnterFoo,
        onLeave: this.handleOnLeaveFoo,
        onUpdate: this.handleOnUpdateFoo
    }
    ...
}

private handleOnEnterFoo(changes: OnEnterStateChanges<States, "foo", StateData, CanLeaveToMap>): void {
    // State buildup logic goes here 
}

private handleOnLeaveFoo(changes: OnLeaveStateChanges<States, "foo" , StateData, CanLeaveToMap>): void {
     // State teardown logic goes here 
}

private handleOnUpdateFoo(changes: OnUpdateStateChanges<States, "foo", StateData, CanLeaveToMap>): void {
    // State update logic goes here 
}

Regular function with multiple states

public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
    foo:{
        ...
        onEnter: this.handleOnEnterFooBar,
        onLeave: this.handleOnLeaveFooBar,
        onUpdate: this.handleOnUpdateFooBar
    },
    bar:{
        ...
        onEnter: this.handleOnEnterFooBar,
        onLeave: this.handleOnLeaveFooBar,
        onUpdate: this.handleOnUpdateFooBar
    }
    ...
}

private handleOnEnterFooBar(changes: OnEnterStateChanges<States, "foo" | "bar", StateData, CanLeaveToMap>): void {
    // States buildup logic goes here 
}

private handleOnLeaveFooBar(changes: OnLeaveStateChanges<States, "foo" | "bar", StateData, CanLeaveToMap>): void {
     // States teardown logic goes here 
}

private handleOnUpdateFooBar(changes: OnUpdateStateChanges<States, "foo" | "bar", StateData, CanLeaveToMap>): void {
    // States update logic goes here  
}

Get Current State

this.currentState$.subscribe((currentStateInfo: CurrentStateInfo<States, StateData, CanLeaveToMap>) => {
    if (currentStateInfo.state === "FSMInit") { return; }
    const currentState: States = currentStateInfo.state;
    switch (currentState) {
        case "foo":
            ...
            break;
        case "bar":
            ...
            break;
        case "baz":
            ...
            break;
        default:
            this.assertCannotReach(currentState);
    }
});

Update State

From currentState$

this.currentState$.subscribe((currentStateInfo: CurrentStateInfo<States, StateData, CanLeaveToMap>) => {
    const { state, stateData } = currentStateInfo;
    if (state === "foo") {
        const { fooProperty } = stateData;
        this.updateState({
            ...stateData,
            fooProperty: fooProperty + 1
        });
    }
});

From Transition Callback

public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
    foo:{
        ...
        onEnter: (changes:OnEnterStateChanges<States, "foo", StateData, CanLeaveToMap>) => {
            const { stateData } = changes.enteringStateInfo;
            const { fooProperty } = stateData;
            this.updateState({
                ...stateData,
                fooProperty: fooProperty + 1
            });
        },
        ...
    }
    ...
}

Change State

From currentState$

this.currentState$.subscribe((currentStateInfo: CurrentStateInfo<States, StateData, CanLeaveToMap>) => {
    const { state, canLeaveTo } = currentStateInfo;
    if (state === "foo" && canLeaveTo.includes("bar")) {
        this.changeState<"foo">({
            state: "bar",
            commonProperty: "some-string",
            barProperty: "some-other-string"
        });
    }
});

From Transition Callback

public override stateMap: StateMap<States, StateData, CanLeaveToMap> = {
    foo:{
        ...
        onEnter: (changes:OnEnterStateChanges<States, "foo", StateData, CanLeaveToMap>) => {
            const { canLeaveTo } = changes.enteringStateInfo;
            if (canLeaveTo.includes("bar")) {
                this.changeState<"foo">({
                    state: "bar",
                    commonProperty: "some-string",
                    barProperty: "some-other-string"
                });
            }
        },
        ...
    }
    ...
}

Unsubscribe Rxjs Helpers

interval(500).pipe(
    takeUntil(this.nextChangeStateTransition$), // Unsubscribes on the next change state transition 
    takeUntil(this.destroy$) // Unsubscribes on destroy
).subscribe(() => {
    ...
});

Get in contact

Submit a bug report

Please visit github/issues to submit a bug report or feature request.

Community

For the latest news and community discussions please visit the github/discussions in the core FsmRx package. This is done to not split the community.