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 anObservable<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.