JSPM

  • Created
  • Published
  • Downloads 243
  • Score
    100M100P100Q119226F
  • License MIT

Package Exports

  • @gernsdorfer/ngrx-lite
  • @gernsdorfer/ngrx-lite/package.json
  • @gernsdorfer/ngrx-lite/testing

Readme

NgrxLite

A small angular State Mangement based on ngrx component-store, with some benefits 😎

Why we need this ?

The current @ngrx/component-store implementation works with its own isolated Store. Unfortunately, there is no connection to the global @ngrx/Store or the @ngrx/store-devtools.

This Library connects your @ngrx/component-store with the @ngrx/Store to share and debug the @ngrx/actions and store.

Benefits

  • 🤝 same API like @ngrx/component-store with optional parameters
  • ⏱ create fast and easy a dynamic redux store
  • ⏳ optional integrated loading state for effects
  • 🤯 debug your application State across different url's
  • ⚒️ Support Redux Devtools for your light components-store (only if you use redux-devtools) for
    • patchState
    • setState
    • createdLoadingEffects
  • 💽 support session/locale Storage
  • 🏘 You Decide where your Store lives: Root, Module or in the Component Scope
  • 🔛 Shared your State Changes and Actions in the ngrx Store
  • 📑 Store your Form Data to persists and debug
  • ✍️ write your tests much easier

Build Status styled with prettier PRs Publish to NPM

UI-DEMO

Open in StackBlitz

Test-DEMO

Open in StackBlitz

Install

yarn: yarn add @ngrx/store @gernsdorfer/ngrx-lite

npm: npm install @ngrx/store @gernsdorfer/ngrx-lite

Usage

  1. Import StoreModulefrom ngrx to your root Module
@NgModule({
  //...
  imports: [StoreModule.forRoot({})]
//...
  1. Create Your Store

You have the same API as @ngrx/component-store

export interface MyState {
  counter: number
}

@Component({
  selector: 'my-component',
  template: '<button (click)="load(\'test\')">',
})
class MyComponent implements OnDestroy {
  // create a componentStore
  private store = this.storeFactory.createComponentStore<MyState>({
    storeName: 'BASIC_COUNTER',
    defaultState: {counter: 0},
  });
  // read the state
  public counterState$: Observable<MyState> = this.store.state$;

  constructor(private storeFactory: StoreFactory) {
  }

  increment(counter: number) {
    // patch your state
    this.store.patchState({counter});
  }

  ngOnDestroy() {
    // destory the store
    this.store.ngOnDestroy();
  }
}

That's it 🥳

Features

Devtool support

Install and import ngrx/store-devtools und have all Features from the devtools for your component store

Let's have a look into the the redux devtools whats going on, in the example above.

Store is init

After the store is init you can find the store in the @ngrx/devtools

State-Init

Patch State

After patch State you see this in your redux devtool. It's possbile to define an custom Actionname for your patch/set State

State-Init

Router Store

Import the RouterStoreModule into your main application to debug your state across all visited URL's. This module store's related URL to the current Store.
So it's possible to replay your state changes by revisiting the related url.

@NgModule({
  //...
  imports: [RouterStoreModule]
//...

Loading Store

Create LoaderStore to set a Loader State while an Effect is running. You have the same API as createComponentStore with an extra methode loadingEffect

type State = LoadingStoreState<{ counter: number }, { message: string }>;

@Component({
  selector: 'my-app-basic-app',
  templateUrl: 'loading-effect.html',
})
export class LoadingEffectComponent implements OnDestroy {
  // create your loading store 
  private store = this.storeFactory.createComponentLoadingStore<State['item'],
    State['error']>({
    storeName: 'LOADING_STORE',
  });

  // read the state
  public counterState$: Observable<State> = this.store.state$;

  // define your loadingEffect to change the state
  public increment = this.store.loadingEffect(
    'increment',
    (counter: number = 0) => of(counter + 1)
  );

  constructor(private storeFactory: StoreFactory) {
  }

  ngOnDestroy() {
    // destory the store
    this.counterStore.ngOnDestroy();
  }
}

What's going on ? Let's have a look into the the redux devtools

Store is init

After the store is init you can find the store in the @ngrx/devtools

State-Init

Loader State isloading changed

For a running Effect isLoading is true and you can show a spinner in your UI.

State-Loading

Effect run successfully

After an Effect run Successfully the item key is updated

State-Success

Effect run unsuccessfully

After an Effect run unsuccessfully the error key contains the error

State-Success

Form Store

interface Product {
  name: string;
}

@Component({
  selector: 'my-app-basic-app',
  templateUrl: 'persist-form.html',
})
export class PersistFormComponent implements OnDestroy {
  productForm = new FormGroup({
    name: new FormControl('', [Validators.required]),
    lastName: new FormControl('', [Validators.required]),
  });
  private store = this.storeFactory.createFormComponentStore<Product>({
    storeName: 'PRODUCT_FORM',
    plugins: {
      storage: 'sessionStoragePlugin',
    },
    formGroup: this.productForm
  });
}

Session/Local Storage

Register Session/Locale-Storage Service

  1. Register Session/Locale-Storage in your Root-Module
@NgModule({
  // ...
  providers: [
    {provide: SessionStoragePlugin, useValue: sessionStoragePlugin},
    {provide: LocalStoragePlugin, useValue: localStoragePlugin}
  ]
  // ...
})
  1. Create your new Store with a session Storage Sync Option
class MyLCass {
  private store = this.storeFactory.createComponentStore<{ counter: number }>({
    storeName: 'SESSION_COUNTER',
    defaultState: {
      counter: 0,
    },
    plugins: {
      storage: 'sessionStoragePlugin',
    },
  });
}

Testing

Import storeTestingFactory and write your test's. A minimal Example you can find here

All Demo Unit Test's you can find here:Open in StackBlitz

TestBed.configureTestingModule({
  //...
  providers: [storeTestingFactory()],
  //..
});