NgSimpleState. Simple state management in Angular with only Services and RxJS.

Nigro Simone - Jun 2 '21 - - Dev Community

One of the most challenging things in software development is state management. Currently there are several state management libraries for Angular apps: NGRX, NGXS, Akita...

But what if you don't want to learn, setup, and deal with all the boilerplate for a simple project, what if you want to manage state by only using tools you already know well as an Angular developer.

In this write up, I'll show you a simple way of managing state by only using RxJS and Dependency Injection with NgSimpleState.

Step 1: install ng-simple-state

npm i ng-simple-state
Enter fullscreen mode Exit fullscreen mode

Step 2: Import NgSimpleStateModule into your AppModule

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { NgSimpleStateModule } from 'ng-simple-state';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    CommonModule,
    NgSimpleStateModule.forRoot({
      enableDevTool: !environment.production, // Enable Redux DevTools only in developing
    }) 
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create your store

This is an example for a counter store in a src/app/counter-store.ts file.
Obviously, you can create every store you want with every complexity you need.

1) Define yuor state interface, eg.:

export interface CounterState {
    count: number;
}
Enter fullscreen mode Exit fullscreen mode

2) Define your store service by extending NgSimpleStateBaseStore, eg.:

import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';

export interface CounterState {
    count: number;
}

@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {

}
Enter fullscreen mode Exit fullscreen mode

3) Implement initialState() method and provide the initial state of the store, eg.:

import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';

export interface CounterState {
    count: number;
}

@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {

  initialState(): CounterState {
    return {
      count: 0
    };
  }

}
Enter fullscreen mode Exit fullscreen mode

4) Implement one or more selectors of the partial state you want, in this example selectCount() eg.:

import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
import { Observable } from 'rxjs';

export interface CounterState {
    count: number;
}

@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {

  initialState(): CounterState {
    return {
      count: 0
    };
  }

  selectCount(): Observable<number> {
    return this.selectState(state => state.count);
  }
}
Enter fullscreen mode Exit fullscreen mode

5) Implement one or more actions for change the store state, in this example increment() and decrement() eg.:

import { Injectable } from '@angular/core';
import { NgSimpleStateBaseStore } from 'ng-simple-state';
import { Observable } from 'rxjs';

export interface CounterState {
  count: number;
}

@Injectable()
export class CounterStore extends NgSimpleStateBaseStore<CounterState> {

  initialState(): CounterState {
    return {
      count: 0
    };
  }

  selectCount(): Observable<number> {
    return this.selectState(state => state.count);
  }

  increment(increment: number = 1): void {
    this.setState(state => ({ count: state.count + increment }));
  }

  decrement(decrement: number = 1): void {
    this.setState(state => ({ count: state.count - decrement }));
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Inject your store into the providers of the module you want (or the providers of component), eg.:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CommonModule } from '@angular/common';
import { NgSimpleStateModule } from 'ng-simple-state';
import { environment } from '../environments/environment';
import { CounterStore } from './counter-store';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    CommonModule,
    NgSimpleStateModule.forRoot({
      enableDevTool: !environment.production, // Enable Redux DevTools only in developing
      enableLocalStorage: false // Local storage state persistence is globally disabled
    })
  ],
  bootstrap: [AppComponent],
  providers: [CounterStore]  // The CounterStore state is shared at AppModule level
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Step 4: Use your store into the components, eg.:

import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { CounterStore } from './counter-store';

@Component({
  selector: 'app-root',
  template: `
  <h1>Counter: {{ counter$ | async }}</h1>
  <button (click)="counterStore.decrement()">Decrement</button>
  <button (click)="counterStore.increment()">Increment</button>
  `,
})
export class AppComponent {
  public counter$: Observable<number>;

  constructor(public counterStore: CounterStore) {
    this.counter$ = this.counterStore.selectCount();
  }
}
Enter fullscreen mode Exit fullscreen mode

That's all!

alt text

see the live demo.

. . . . . . .
Terabox Video Player