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
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 {}
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; }
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> { }
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 }; } }
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); } }
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 })); } }
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 {}
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(); } }
That's all!
see the live demo.
Top comments (0)