@stefantirea/ngrx-helper
TypeScript icon, indicating that this package has built-in type declarations

0.1.1 • Public • Published

NGRX Helper Library

About

This NGRX Helper Library includes only convenience methods to avoid actions, reducers, effects & selectors boilerplate code (for http calls).

On top of that, some miscellaneous features are also included:

  • Wrapper Type for loading Data (States: value: T, isLoading: boolean & error: HttpErrorResponse)
  • Auto-Unsubscribe subscriptions on Component Destroy
  • *ngLet Directive as an alternative to *ngIf but without removing the view when the value is falsy
  • AutoUnsubscribe from an Observable<T> on Angular Component Destroy Lifecycle

Usage

Ngrx Helper

Reduce boilerplate code with convenient ngrx methods, which generate the required actions, reducers & effects. On top of that, loading/error handling is included in the reducers & effects. The result is mapped into the LazyValue<T> wrapper type.

1. Initial setup of the Store, Actions & Reducers

import {LazyValue, createHttpReducer, createHttpActions} from 'ngrx-helper';
import {createFeatureSelector, createSelector} from '@ngrx/store';

export interface ProjectState {
  project?: LazyValue<Project>;
}

const initialState: ProjectState = {
  project: undefined
}

// {request, response, error, reset} Generates 4 Actions
// string => request payload type; Project => response payload type
export const projectLoadActions = createHttpActions<string, Project>('[PROJECT] Project by ID');

// generates 4 reducers which listens to all actions above (loading, value, error, empty)
export const appReducer = createHttpReducer(projectLoadActions, initialState, 'project');

// helper ngrx selector to avoid duplicate code
const selector = createSelectorHelper<ProjectState>('appReducer');
export const selectProject = selector(state => state.project);

2. Listen on projectLoadActions and bind http service call to response action

import {HttpClient} from '@angular/common/http';
import {createHttpEffect} from 'ngrx-helper';
import {Actions} from '@ngrx/effects';

@Injectable()
export class ProjectEffects {

  project$ = createHttpEffect<string, Project>(
    this.actions$,
    projectLoadActions,
    (id: string) => this.http.get<Project>(`/api/projects/${id}`, {observe: 'response'})
  );

  constructor(private actions$: Actions,
              private http: HttpClient) {
  }
}

3. Use it in the application just like NGRX. Don't forget to include the reducers & effects in the Angular Module.

@Component({...})
export class ProjectHelperComponent {

  project$: Observable<LazyValue<Project> | undefined>;

  constructor(private store: Store) {
    store.dispatch(projectLoadActions.request({})); // call backend
    this.project$ = store.select(selectProjects);
  }

  // example reset action (sets project to empty)
  resetProject() {
    this.store.dispatch(projectLoadActions.reset({}));
  }
}

In case any error happens or you want to show something while its loading, all the information needed are included in the LazyValue<T> wrapper:

export class LazyValue<T> {
  constructor(public value: T | undefined,
              public isLoading: boolean,
              public error: HttpErrorResponse | undefined) {
  }
}

NgLet Directive

Handling the subscription in the view with the async pipe in combination with *ngIf="... let value" is the goto solution when working with Observable<T>.

However, *ngIf removes the view when the value is falsy. To prevent this, the *ngLet directive behaves the same as *ngIf but the view is visible at all times.

Example:

import {Observable} from "rxjs";
import {LazyValue} from "./lazy-value";
import {Component} from "@angular/core";
import {Store} from "@ngrx/store";

@Component()
export class ProjectView {
  // value can be LazyValue<string>, undefined & null when resolved with async
  selectedProject$: Observable<LazyValue<string> | undefined>;

  constructor(private store: Store) {
    this.selectedProject$ = store.select(selectProjectId);
  }
}
// no matter the value, the view is always shown
<div *ngLet="selectedProject$ | async let projectId">
  <p *ngIf="projectId">Selected Project ID: {{projectId}}</p>
  <p *ngIf="!projectId">No Selection</p>
</div>

Auto-Unsubscribe

Sometimes an Observable<T> has to be subscribed in the component and you have to handle the tedious task of unsubscribing.

To simplify this, the component can extend from the AutoUnsubscribe class. It includes the autoUnsubscribe(...) method which can be wrapped around an Observable<T> before subscribing.

Example Usage:

import {Observable} from "rxjs";
import {Component} from "@angular/core";
import {AutoUnsubscribe} from "./auto-unsubscribe";

@Component()
export class ProjectView extends AutoUnsubscribe {

  constructor() {
    super();
    const value$: Observable<number> = of(1, 2, 3);
    // subscription cleaned up on component destroy
    this.autoUnsubscribe(value$)
      .subscribe(value => console.log(value));
  }
}

Limitations

The NGRX Helper methods requires using the built-in wrapper type LazyValue<T>. Currently, there is no other way around it.

Package Sidebar

Install

npm i @stefantirea/ngrx-helper

Weekly Downloads

1

Version

0.1.1

License

MIT

Unpacked Size

113 kB

Total Files

25

Last publish

Collaborators

  • stefantirea