Gentleman State Manager, the savior you need for Angular applications

Alan Buscaglia
Mar 27 · 6 min read

Hi everyone ! I often found myself creating custom solutions to state management problems for my junior coworkers, because lets get real about this…. reactive programming and ngrx/store are pretty difficult to understand and more to master.

I LOVE Rxjs ! customizing flows of data trough observables and combining their results are one of my guilty pleasures in development, but not everyone feels the same when having to share information between brother components or different modules. You may often use services, input and outputs, or even create Rxjs’s observables to try to fix this, but as the application grows ngrx/store becomes a sweet choice….if you and your teammates have the sufficient experience for the challenge.

So…what happens if you are like me with an application that needs the sharing of information, and that could benefit of ngrx/store, but you or your teammates doesn't have enough experience to use it ?

First…Let’s learn some things !

Imagine an empty tube with 3 holes in it, at each hole we have a person looking trough it and waiting for a little ball to pass. Because each person is different, they will see different aspects of the ball. For example, one can see that the ball is red, another that the ball is bouncing a little when it’s passing trough …or even that maybe it’s not a ball at all !.

That’s exactly what reactive programming is ! we have an information channel (observables) containing the data that is passing trough it, and on the other hand we have entities (the angular components for example) that will be subscribed and waiting for that information to pass …and when it does, they will react to it doing what ever business logic we coded for them.

Redux, Ngrx/store, etc. are libraries that provide tools to manage the state of the application using the concept of “A Single Source Of Truth”, these means that the whole information of the app will be contained inside a “store”, a place where you can access useful and up to date information.

Each time a change happens inside your application, information wise, the store will be updated with the latest changes. So you don’t need to call the back end again for an update if not needed and also all entities that are watching the store’s states will be automatically alerted of the change.

Rxjs is a library containing a lot of amazing tools to manage these information channels, these provide the ability to create, combine and modify observables with the inclusion of new kinds to use, each one with their own functionalities.

We are going to use Behaviour Subjects a lot in this case, it’s an observable that will always contain the last data that passed trough it….which is amazing for what we want to do ! When ever we send information trough the Behaviour Subject, even if an entity subscribes AFTER the information has been sent, it will receive the latest data ! so we don’t loose anything.

So…What’s Gentleman State Manager ?

But don’t worry, the main idea of the library is to be easy to use and more than everything…transparent, you can access the array data at any place to get or send the information stored inside with a super easy and useful API that is detailed next.

How to use

state.ts for Root ( AppModule ):

export interface FirstState {
stateProperty: string | null;
}

export enum FirstStateProperties {
STATEPROPERTY: 'stateProperty'
}

export enum StoreKeys {
FIRSTSTATE: 'firstState'
}

AppModule:

const sourceOfTruthInitiate: SourceOfTruthInitiate[] = [
{
key: StoreKeys.FIRSTSTATE,
state: {
stateProperty: 'your state property value'
},
stateProperties: FirstStateProperties
}
];
  • key: represents the key to access the proper observable that we want to use.
  • state: the empty state of the object that will pass trough the observable.
  • stateProperties: the properties of the object, we are going to use it to access the information without ambiguities.

2- import GentlemanStateManagerModule inside your application in the following way:

@NgModule({
imports: [
...
GentlemanStateManagerModule.forRoot(sourceOfTruthInitiate)
...
]
})
export class AppModule {}

3- use our API to access the information or send something new !

Example:

export class OverviewMetricComponent implements OnDestroy {
constructor(gentlemanStateManager: GentlemanStateService) {
console.log(gentlemanStateManager.getEntity(StoreKeys.FIRSTSTATE).getPropertyFromState(FirstStateProperties.STATEPROPERTY));
}
}

4- to lazy load more observables just do the same functionality as in the app module, but in the constructor of your lazy loaded one.

Example:

state.ts for Root ( AppModule ):

...
export enum StoreKeys {
FIRSTSTATE: 'firstState',
LAZYLOADEDSTATE: 'lazyLoadedState' // we add the new state key
}
...

state.ts of your lazy loaded module:

export interface LazyLoadedState {
lazyLoadedStateProperty: string | null;
}

export enum LazyLoadedStateProperties {
LAZYLOADEDSTATEPROPERTY: 'lazyLoadedStateProperty'
}

const sourceOfTruthInitiate: SourceOfTruthInitiate[] = [
{
key: StoreKeys.LAZYLOADEDSTATE,
state: {
lazyLoadedStateProperty: 'Your Lazy Loaded State Property'
},
stateProperties: LazyLoadedStateProperties
}
];

@NgModule({
...
})
export class LazyLoadedModule {
constructor(gentlemanStateService: GentlemanStateService) {
sourceOfTruthInitiate.forEach(state => gentlemanStateService.createObservable(state.key, state.state, state.stateProperties));
}
}

API

Array Of Observables Management :

/**
* @desc it creates and observable and adds it to the observable array.
* @param key: the key to be used to represent the observable item inside the array
* @param state: the state of the observable, the object that represents what the observable is going to contain
* @return void
*/
createObservable(key: string, state: any, stateProperties: StateProperties): void

getEntity

/**
* @desc it returns the selected observable using the provided key.
* @param key - the key to be used to represent the observable item inside the array
* @return GentlemanStateObject
*/
getEntity(key: string): GentlemanStateObject

destroyObservable

/**
* @desc it destroys an object from the observable array.
* @param key - the key to be used to represent the observable item inside the array
* @return void
*/
destroyObservable(key: string): void

GentlemanStateObject :

/**
* @desc returns the observable that contains the state for async operations - it listens for changes
* @return Observable<any>
*/
getObservable(): Observable<any>

getStateProperties

/**
* @desc returns the state properties object
* @return StateProperties
*/
getStateProperties(): StateProperties

unsubscribe (use it when you are destroying the main component of your module)

/**
* @desc unsubscribes from the observable
* @return void
*/
unsubscribe(): void

getStateSnapshot

/**
* @desc returns the value of the state at the time of the call
* @return any
*/
getStateSnapshot(): any

getPropertyFromState

/**
* @desc returns the value of a property of the state at the time of the call
* @param property - the name of the requested property
* @return any
*/
getPropertyFromState(property: string): any

getPropertyFromObservable

/**
* @desc returns the value of a property of the state for async operations - it listens for changes
* @param property - the name of the requested property
* @return Observable
*/
getPropertyFromObservable(property: string): Observable<any>

setObservableValues

/**
* @desc sets the value for a certain property inside the state, triggers an async event
* @param value - the value for the requested property
* @param property - the name of the requested property
* @param emit - if true it will trigger an async event
* @return void
*/
setObservableValues(value: any, property: string | null = null, emit = true): void

setStateValues

/**
* @desc sets the value for a certain property inside the state, doesn't triggers an async event
* @param value - the value for the requested property
* @param property - the name of the requested property, if no property it will try to patch the values into the state
* @return void
*/
setStateValues(value: any, property: string | null): void

resetState

/**
* @desc resets the state
* @return void
*/
resetState(): void

Thanks everyone for reading ! it´s a pleasure helping the community anyway I can !

I invite you all to download Gentleman State Manager and give it a star on Github ! its open source and you can do anything you want to it, it´s going to be an honor if you want to fork it to test some things of your own :)

NPM: gentleman-state-manager — npm (npmjs.com)

Github: AppleLAN/gentleman-state-manager

I want also to invite you to my Front End Community over Discord ! where I do mentoring and give classes trough my YouTube channel.

Happy coding everyone !!

Nerd For Tech

From Confusion to Clarification

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/.

Alan Buscaglia

Written by

I'm an Engineer Front End Developer Architect with expertise in Angular with ngrx/store. Huge experience in big data flow applications.

Nerd For Tech

NFT is an Educational Media House. Our mission is to bring the invaluable knowledge and experiences of experts from all over the world to the novice. To know more about us, visit https://www.nerdfortech.org/.