0% found this document useful (0 votes)
9 views

dev_to_angular_ngrxbest_practices_for_enterprise_angular_app

This article outlines best practices for implementing NgRx in enterprise Angular applications, emphasizing the importance of clean separation of concerns between store feature slices and the root store. It provides a structured approach to creating Root Store Modules and Feature Store Modules, along with detailed implementation steps for actions, state, reducers, selectors, and effects. The article serves as a guideline for developers to enhance their understanding and application of NgRx in Angular projects.

Uploaded by

srikanth.n24007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

dev_to_angular_ngrxbest_practices_for_enterprise_angular_app

This article outlines best practices for implementing NgRx in enterprise Angular applications, emphasizing the importance of clean separation of concerns between store feature slices and the root store. It provides a structured approach to creating Root Store Modules and Feature Store Modules, along with detailed implementation steps for actions, state, reducers, selectors, and effects. The article serves as a guideline for developers to enhance their understanding and application of NgRx in Angular projects.

Uploaded by

srikanth.n24007
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

Wes for Angular

Posted on Feb 4, 2019 • Updated on Jun 12, 2019 • Originally published at wesleygrimes.com

16 3

NgRx — Best Practices for Enterprise Angular Applications


#angular #ngrx #javascript #redux

Before We Get Started


This article is not intended to be a tutorial on NgRx. There are several great resources that currently
exist, written by experts much smarter than me. I highly suggest that you take time and learn NgRx and
the redux pattern before attempting to implement these concepts.

Ultimate Angular — NgRx Store & Effects


Pluralsight — Play by Play Angular NgRx
NgRx Blog on Medium.com
NgRx.io Docs
NgRx.io Resources

Background
The following represents a pattern that I’ve developed at my day job after building several enterprise
Angular applications using the NgRx library. I have found that most online tutorials do a great job of
helping you to get your store up and running, but often fall short of illustrating best practices for clean
separation of concerns between your store feature slices, root store, and user interface.

With the following pattern, your root application state, and each slice (property) of that root application
state are separated into a RootStoreModule and per feature MyFeatureStoreModule.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Prerequisites
This article assumes that you are building an Angular v6 CLI generated application.

Installing NgRx Dependencies


Before we get started with generating code, let’s make sure to install the necessary NgRx node
modules from a prompt:

npm install @ngrx/{store,store-devtools,entity,effects}

Best Practice #1 — The Root Store Module


Create a Root Store Module as a proper Angular NgModule’s that bundles together NgRx store logic.
Feature store modules will be imported into the Root Store Module allowing for a single root store
module to be imported into your application’s main App Module.

Suggested Implementation
1. Generate RootStoreModule using the Angular CLI:

ng g module root-store —-flat false —-module app.module.ts

2. Generate RootState interface to represent the entire state of your application using the Angular CLI:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
ng g interface root-store/root-state

This will create an interface named RootState but you will need to rename it to State inside the
generated .ts file as we want to, later on, utilize this as RootStoreState.State

PLEASE NOTE: You will come back later on and add to this interface each feature module as a
property.

Best Practice #2 — Create Feature Store Module(s)


Create feature store modules as proper Angular NgModule’s that bundle together feature slices of your
store, including state, actions, reducer, selectors, and effects. Feature modules are then imported into
your RootStoreModule. This will keep your code cleanly organizing into sub-directories for each feature
store. In addition, as illustrated later on in the article, public actions, selectors, and state are name-
spaced and exported with feature store prefixes.

Naming Your Feature Store


In the example implementation below we will use the feature name MyFeature, however, this will be
different for each feature you generate and should closely mirror the RootState property name. For
example, if you are building a blog application, a feature name might be Post.

Entity Feature Modules or Standard Feature Modules?


Depending on the type of feature you are creating you may or may not benefit from implementing NgRx
Entity. If your store feature slice will be dealing with an array of type then I suggest following the Entity
Feature Module implementation below. If building a store feature slice that does not consist of a
standard array of type, then I suggest following the Standard Feature Module implementation below.

Suggested Implementation — Entity Feature Module


1. Generate MyFeatureStoreModule feature module using the Angular CLI:

ng g module root-store/my-feature-store --flat false --module root-store/root-store.module.ts

2. Actions — Create an actions.ts file in the app/root-store/my-feature-store directory:

1 import { Action } from '@ngrx/store';


2 import { MyModel } from '../../models';
3
4 export enum ActionTypes {
5 LOAD_REQUEST = '[My Feature] Load Request',
6 LOAD_FAILURE = '[My Feature] Load Failure',
7 LOAD_SUCCESS = '[My Feature] Load Success'
8 }
9
10 export class LoadRequestAction implements Action {
11 readonly type = ActionTypes.LOAD_REQUEST;
12 }
13
14 export class LoadFailureAction implements Action {
15 readonly type = ActionTypes.LOAD_FAILURE;
16 constructor(public payload: { error: string }) {}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
17 }
18
19 export class LoadSuccessAction implements Action {
20 readonly type = ActionTypes.LOAD_SUCCESS;
21 constructor(public payload: { items: MyModel[] }) {}
22 }
23
24 export type Actions = LoadRequestAction | LoadFailureAction | LoadSuccessAction;

entity-feature-store-module-actions.ts hosted with ❤ by GitHub view raw

3. State — Create a state.ts file in the app/root-store/my-feature-store directory:

1 import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity';


2 import { MyModel } from '../../models';
3
4 export const featureAdapter: EntityAdapter<
5 MyModel
6 > = createEntityAdapter<MyModel>({
7 selectId: model => model.id,
8 sortComparer: (a: MyModel, b: MyModel): number =>
9 b.someDate.toString().localeCompare(a.someDate.toString())
10 });
11
12 export interface State extends EntityState<MyModel> {
13 isLoading?: boolean;
14 error?: any;
15 }
16
17 export const initialState: State = featureAdapter.getInitialState(
18 {
19 isLoading: false,
20 error: null
21 }
22 );

entity-feature-store-module-state.ts hosted with ❤ by GitHub view raw

4. Reducer — Create a reducer.ts file in the app/root-store/my-feature-store directory:

1 import { Actions, ActionTypes } from './actions';


2 import { featureAdapter, initialState, State } from './state';
3
4 export function featureReducer(state = initialState, action: Actions): State {
5 switch (action.type) {
6 case ActionTypes.LOAD_REQUEST: {
7 return {
8 ...state,
9 isLoading: true,
10 error: null
11 };
12 }
13 case ActionTypes.LOAD_SUCCESS: {
14 return featureAdapter.addAll(action.payload.items, {
15 ...state,
16 isLoading: false,

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
17 error: null
18 });
19 }
20 case ActionTypes.LOAD_FAILURE: {
21 return {
22 ...state,
23 isLoading: false,
24 error: action.payload.error
25 };
26 }
27 default: {
28 return state;
29 }
30 }
31 }

entity-feature-store-module-reducer.ts hosted with ❤ by GitHub view raw

5. Selectors — Create a selectors.ts file in the app/root-store/my-feature-store directory:

1 import {
2 createFeatureSelector,
3 createSelector,
4 MemoizedSelector
5 } from '@ngrx/store';
6
7 import { MyModel } from '../models';
8
9 import { featureAdapter, State } from './state';
10
11 export const getError = (state: State): any => state.error;
12
13 export const getIsLoading = (state: State): boolean => state.isLoading;
14
15 export const selectMyFeatureState: MemoizedSelector<
16 object,
17 State
18 > = createFeatureSelector<State>('myFeature');
19
20 export const selectAllMyFeatureItems: (
21 state: object
22 ) => MyModel[] = featureAdapter.getSelectors(selectMyFeatureState).selectAll;
23
24 export const selectMyFeatureById = (id: string) =>
25 createSelector(this.selectAllMyFeatureItems, (allMyFeatures: MyModel[]) => {
26 if (allMyFeatures) {
27 return allMyFeatures.find(p => p.id === id);
28 } else {
29 return null;
30 }
31 });
32
33 export const selectMyFeatureError: MemoizedSelector<object, any> = createSelector(
34 selectMyFeatureState,
35 getError
36 );

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
37
38 export const selectMyFeatureIsLoading: MemoizedSelector<
39 object,
40 boolean
41 > = createSelector(selectMyFeatureState, getIsLoading);

entity-feature-module-selectors.ts hosted with ❤ by GitHub view raw

6. Effects — Create an effects.ts file in the app/root-store/my-feature-store directory with the following:

1 import { Injectable } from '@angular/core';


2 import { Actions, Effect, ofType } from '@ngrx/effects';
3 import { Action } from '@ngrx/store';
4 import { Observable, of as observableOf } from 'rxjs';
5 import { catchError, map, startWith, switchMap } from 'rxjs/operators';
6 import { DataService } from '../../services/data.service';
7 import * as featureActions from './actions';
8
9 @Injectable()
10 export class MyFeatureStoreEffects {
11 constructor(private dataService: DataService, private actions$: Actions) {}
12
13 @Effect()
14 loadRequestEffect$: Observable<Action> = this.actions$.pipe(
15 ofType<featureActions.LoadRequestAction>(
16 featureActions.ActionTypes.LOAD_REQUEST
17 ),
18 startWith(new featureActions.LoadRequestAction()),
19 switchMap(action =>
20 this.dataService
21 .getItems()
22 .pipe(
23 map(
24 items =>
25 new featureActions.LoadSuccessAction({
26 items
27 })
28 ),
29 catchError(error =>
30 observableOf(new featureActions.LoadFailureAction({ error }))
31 )
32 )
33 )
34 );
35 }

entity-feature-store-module-effects.ts hosted with ❤ by GitHub view raw

Suggested Implementation — Standard Feature Module


1. Generate MyFeatureStoreModule feature module using the Angular CLI:

ng g module root-store/my-feature-store --flat false --module root-store/root-store.module.ts

2. Actions — Create an actions.ts file in the app/root-store/my-feature-store directory:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
1 import { Action } from '@ngrx/store';
2 import { User } from '../../models';
3
4 export enum ActionTypes {
5 LOGIN_REQUEST = '[My Feature] Login Request',
6 LOGIN_FAILURE = '[My Feature] Login Failure',
7 LOGIN_SUCCESS = '[My Feature] Login Success'
8 }
9
10 export class LoginRequestAction implements Action {
11 readonly type = ActionTypes.LOGIN_REQUEST;
12 constructor(public payload: { userName: string; password: string }) {}
13 }
14
15 export class LoginFailureAction implements Action {
16 readonly type = ActionTypes.LOGIN_FAILURE;
17 constructor(public payload: { error: string }) {}
18 }
19
20 export class LoginSuccessAction implements Action {
21 readonly type = ActionTypes.LOGIN_SUCCESS;
22 constructor(public payload: { user: User }) {}
23 }
24
25 export type Actions = LoginRequestAction | LoginFailureAction | LoginSuccessAction;

standard-feature-store-module-actions.ts hosted with ❤ by GitHub view raw

3. State — Create a state.ts file in the app/root-store/my-feature-store directory:

1 import { User } from '../../models';


2
3 export interface State {
4 user: User | null;
5 isLoading: boolean;
6 error: string;
7 }
8
9 export const initialState: State = {
10 user: null,
11 isLoading: false,
12 error: null
13 }

standard-feature-store-module-state.ts hosted with ❤ by GitHub view raw

4. Reducer — Create a reducer.ts file in the app/root-store/my-feature-store directory:

1 import { Actions, ActionTypes } from './actions';


2 import { initialState, State } from './state';
3
4 export function featureReducer(state = initialState, action: Actions): State {
5 switch (action.type) {
6 case ActionTypes.LOGIN_REQUEST:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
7 return {
8 ...state,
9 error: null,
10 isLoading: true
11 };
12 case ActionTypes.LOGIN_SUCCESS:
13 return {
14 ...state,
15 user: action.payload.user,
16 error: null,
17 isLoading: false,
18
19 };
20 case ActionTypes.LOGIN_FAILURE:
21 return {
22 ...state,
23 error: action.payload.error,
24 isLoading: false
25 };
26 default: {
27 return state;
28 }
29 }
30 }

standard-feature-store-module-reducer.ts hosted with ❤ by GitHub view raw

5. Selectors — Create a selectors.ts file in the app/root-store/my-feature-store directory:

1 import {
2 createFeatureSelector,
3 createSelector,
4 MemoizedSelector
5 } from '@ngrx/store';
6
7 import { User } from '../../models';
8
9 import { State } from './state';
10
11 const getError = (state: State): any => state.error;
12
13 const getIsLoading = (state: State): boolean => state.isLoading;
14
15 const getUser = (state: State): any => state.user;
16
17 export const selectMyFeatureState: MemoizedSelector<
18 object,
19 State
20 > = createFeatureSelector<State>('myFeature');
21
22 export const selectMyFeatureError: MemoizedSelector<object, any> = createSelector(
23 selectMyFeatureState,
24 getError
25 );
26
27 export const selectMyFeatureIsLoading: MemoizedSelector<

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
28 object,
29 boolean
30 > = createSelector(selectMyFeatureState, getIsLoading);
31
32 export const selectMyFeatureUser: MemoizedSelector<
33 object,
34 User
35 > = createSelector(selectMyFeatureState, getUser);

standard-feature-store-module-selectors.ts hosted with ❤ by GitHub view raw

6. Effects — Create an effects.ts file in the app/root-store/my-feature-store directory with the following:

1 import { Injectable } from '@angular/core';


2 import { Actions, Effect, ofType } from '@ngrx/effects';
3 import { Action } from '@ngrx/store';
4 import { Observable, of as observableOf } from 'rxjs';
5 import { catchError, map, startWith, switchMap } from 'rxjs/operators';
6 import { DataService } from '../../services/data.service';
7 import * as featureActions from './actions';
8
9 @Injectable()
10 export class MyFeatureStoreEffects {
11 constructor(private dataService: DataService, private actions$: Actions) {}
12
13 @Effect()
14 loginRequestEffect$: Observable<Action> = this.actions$.pipe(
15 ofType<featureActions.LoginRequestAction>(
16 featureActions.ActionTypes.LOGIN_REQUEST
17 ),
18 switchMap(action =>
19 this.dataService
20 .login(action.payload.userName, action.payload.password)
21 .pipe(
22 map(
23 user =>
24 new featureActions.LoginSuccessAction({
25 user
26 })
27 ),
28 catchError(error =>
29 observableOf(new featureActions.LoginFailureAction({ error }))
30 )
31 )
32 )
33 );
34 }

standard-feature-store-module-effects.ts hosted with ❤ by GitHub view raw

Suggested Implementation — Entity and Standard Feature Modules


Now that we have created our feature module, either Entity or Standard typed above, we need to import
the parts (state, actions, reducer, effects, selectors) into the Angular NgModule for the feature. In
addition, we will create a barrel export in order to make imports in our application components clean
and orderly, with asserted name-spaces.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
1. Update the app/root-store/my-feature-store/my-feature-store.module.ts with the following:

1 import { CommonModule } from '@angular/common';


2 import { NgModule } from '@angular/core';
3 import { EffectsModule } from '@ngrx/effects';
4 import { StoreModule } from '@ngrx/store';
5 import { MyFeatureStoreEffects } from './effects';
6 import { featureReducer } from './reducer';
7
8 @NgModule({
9 imports: [
10 CommonModule,
11 StoreModule.forFeature('myFeature', featureReducer),
12 EffectsModule.forFeature([MyFeatureStoreEffects])
13 ],
14 providers: [MyFeatureStoreEffects]
15 })
16 export class MyFeatureStoreModule {}

feature-store-module.ts hosted with ❤ by GitHub view raw

2. Create an app/root-store/my-feature-store/index.ts barrel export. You will notice that we import our
store components and alias them before re-exporting them. This, in essence, is “name-spacing” our
store components.

1 import * as MyFeatureStoreActions from './actions';


2 import * as MyFeatureStoreSelectors from './selectors';
3 import * as MyFeatureStoreState from './state';
4
5 export {
6 MyFeatureStoreModule
7 } from './my-feature-store.module';
8
9 export {
10 MyFeatureStoreActions,
11 MyFeatureStoreSelectors,
12 MyFeatureStoreState
13 };

feature-store-module-index.ts hosted with ❤ by GitHub view raw

Best Practice #1 — The Root Store Module (cont.)


Now that we have built our feature modules, let’s pick up where we left off in best practice #1 and
finish building out our RootStoreModule and RootState.

Suggested Implementation (cont.)


3. Update app/root-store/root-state.ts and add a property for each feature that we have created
previously:

1 import { MyFeatureStoreState } from './my-feature-store';


2 import { MyOtherFeatureStoreState } from './my-other-feature-store';
3

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
4 export interface State {
5 myFeature: MyFeatureStoreState.State;
6 myOtherFeature: MyOtherFeatureStoreState.State;
7 }

root-state.ts hosted with ❤ by GitHub view raw

4. Update your app/root-store/root-store.module.ts by importing all feature modules, and importing the
following NgRx modules: StoreModule.forRoot({}) and EffectsModule.forRoot([]):

1 import { CommonModule } from '@angular/common';


2 import { NgModule } from '@angular/core';
3 import { EffectsModule } from '@ngrx/effects';
4 import { StoreModule } from '@ngrx/store';
5 import { MyFeatureStoreModule } from './my-feature-store/';
6 import { MyOtherFeatureStoreModule } from './my-other-feature-store/';
7
8 @NgModule({
9 imports: [
10 CommonModule,
11 MyFeatureStoreModule,
12 MyOtherFeatureStoreModule,
13 StoreModule.forRoot({}),
14 EffectsModule.forRoot([])
15 ],
16 declarations: []
17 })
18 export class RootStoreModule {}

root-store-module.ts hosted with ❤ by GitHub view raw

5. Create an app/root-store/selectors.ts file. This will hold any root state level selectors, such as a
Loading property, or even an aggregate Error property:

1 import { createSelector, MemoizedSelector } from '@ngrx/store';


2 import {
3 MyFeatureStoreSelectors
4 } from './my-feature-store';
5
6 import {
7 MyOtherFeatureStoreSelectors
8 } from './my-other-feature-store';
9
10 export const selectError: MemoizedSelector<object, string> = createSelector(
11 MyFeatureStoreSelectors.selectMyFeatureError,
12 MyOtherFeatureStoreSelectors.selectMyOtherFeatureError,
13 (myFeatureError: string, myOtherFeatureError: string) => {
14 return myFeature || myOtherFeature;
15 }
16 );
17
18 export const selectIsLoading: MemoizedSelector<
19 object,
20 boolean
21 > = createSelector(

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
22 MyFeatureStoreSelectors.selectMyFeatureIsLoading,
23 MyOtherFeatureStoreSelectors.selectMyOtherFeatureIsLoading,
24 (myFeature: boolean, myOtherFeature: boolean) => {
25 return myFeature || myOtherFeature;
26 }
27 );

selectors.ts hosted with ❤ by GitHub view raw

6. Create an app/root-store/index.ts barrel export for your store with the following:

1 import { RootStoreModule } from './root-store.module';


2 import * as RootStoreSelectors from './selectors';
3 import * as RootStoreState from './state';
4 export * from './my-feature-store';
5 export * from './my-other-feature-store';
6 export { RootStoreState, RootStoreSelectors, RootStoreModule };

index.ts hosted with ❤ by GitHub view raw

Wiring up the Root Store Module to your Application


Now that we have built our Root Store Module, composed of Feature Store Modules, let’s add it to the
main app.module.ts and show just how neat and clean the wiring up process is.

1. Add RootStoreModule to your application’s NgModule.imports array. Make sure that when you import
the module to pull from the barrel export:

import { RootStoreModule } from ‘./root-store’;

2. Here’s an example container component that is using the store:

1 import { Component, OnInit } from '@angular/core';


2 import { Store } from '@ngrx/store';
3 import { Observable } from 'rxjs';
4 import { MyModel } from '../../models';
5 import {
6 RootStoreState,
7 MyFeatureStoreActions,
8 MyFeatureStoreSelectors
9 } from '../../root-store';
10
11 @Component({
12 selector: 'app-my-feature',
13 styleUrls: ['my-feature.component.css'],
14 templateUrl: './my-feature.component.html'
15 })
16 export class MyFeatureComponent implements OnInit {
17 myFeatureItems$: Observable<MyModel[]>;
18 error$: Observable<string>;
19 isLoading$: Observable<boolean>;
20
21 constructor(private store$: Store<RootStoreState.State>) {}
22
23 ngOnInit() {

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
24 this.myFeatureItems$ = this.store$.select(
25 MyFeatureStoreSelectors.selectAllMyFeatureItems
26 );
27
28 this.error$ = this.store$.select(
29 MyFeatureStoreSelectors.selectUnProcessedDocumentError
30 );
31
32 this.isLoading$ = this.store$.select(
33 MyFeatureStoreSelectors.selectUnProcessedDocumentIsLoading
34 );
35
36 this.store$.dispatch(
37 new MyFeatureStoreActions.LoadRequestAction()
38 );
39 }
40 }

example-container.component.ts hosted with ❤ by GitHub view raw

Finished Application Structure


Once we have completed implementation of the above best practices our Angular application structure
should look very similar to something like this:

├── app
│ ├── app-routing.module.ts
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── components
│ ├── containers
│ │ └── my-feature
│ │ ├── my-feature.component.css
│ │ ├── my-feature.component.html
│ │ └── my-feature.component.ts
│ ├── models
│ │ ├── index.ts
│ │ └── my-model.ts
│ │ └── user.ts
│ ├── root-store
│ │ ├── index.ts
│ │ ├── root-store.module.ts
│ │ ├── selectors.ts
│ │ ├── state.ts
│ │ └── my-feature-store
│ │ | ├── actions.ts
│ │ | ├── effects.ts
│ │ | ├── index.ts
│ │ | ├── reducer.ts
│ │ | ├── selectors.ts
│ │ | ├── state.ts
│ │ | └── my-feature-store.module.ts
│ │ └── my-other-feature-store
│ │ ├── actions.ts

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
│ │ ├── effects.ts
│ │ ├── index.ts
│ │ ├── reducer.ts
│ │ ├── selectors.ts
│ │ ├── state.ts
│ │ └── my-other-feature-store.module.ts
│ └── services
│ └── data.service.ts
├── assets
├── browserslist
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── tslint.json

Fully Working Example — Chuck Norris Joke Generator


I have put together a fully working example of the above best practices. It’s a simple Chuck Norris Joke
Generator that has uses @angular/material and the http://www.icndb.com/ api for data.

Github

wesleygrimes / angular-ngrx-chuck-norris
Chuck Norris Joke Generator w/ NgRx Store

Ultimate Courses

Angular NgRx Chuck Norris Joke Generator


This project is a Chuck Norris Joke Generator backed by an NgRx Store using best practices as described in this article: Link to
article

This project was generated with Angular CLI version 7.3.3.

Development server
Run ng serve for a dev server. Navigate to http://localhost:4200/ . The app will automatically reload if you change any of the
source files.

Code scaffolding
Run ng generate component component-name to generate a new component. You can also use ng generate
directive|pipe|service|class|guard|interface|enum|module .

Build
View on GitHub

Stackblitz
You can see the live demo at https://angular-ngrx-chuck-norris.stackblitz.io and here is the Stackblitz
editor:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
angular-ngrx-chuck-norris - StackBlitz
_NgRx _Best_Practices_Chuck_Norris_Example_stackblitz.com

Conclusion
It’s important to remember that I have implemented these best practices in several “real world”
applications. While I have found these best practices helpful, and maintainable, I do not believe they
are an end-all-be-all solution to organizing NgRx projects; it’s just what has worked for me. I am curious
as to what you all think? Please feel free to offer any suggestions, tips, or best practices you’ve learned
when building enterprise Angular applications with NgRx and I will update the article to reflect as such.
Happy Coding!

Additional Resources
I would highly recommend enrolling in the Ultimate Angular courses, especially the NgRx course. It is
well worth the money and I have used it as a training tool for new Angular developers. Follow the link
below to signup.

Ultimate Courses: Expert online courses in JavaScript, Angular, NGRX and TypeScript
_Expert online courses in JavaScript, Angular, NGRX and TypeScript. Join 50,000 others mastering new
technologies with…_ultimatecourses.com

👋 Before you go
Please leave your appreciation by commenting on this post!

It takes just one minute and is worth it for your career.

Get started

Top comments (14) Subscribe

• Feb 14 '19

Hello,
I have two features in my store: Auth and Itsm. After a successful connection with Auth, I want
to send the first action of Itsm. But there is a circular dependency.

I think I have built all the stores by following the tutorial. If I use the router directly in Auth-
Effects, it works well.

Do you have an example with two features and one calling the second?

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Thank you for this tutorial which is what I needed.
Daniel

• Feb 14 '19

Great Question Daniel! You should be able to dispatch actions in other features from effects.
Is this not working?

• Feb 14 '19

Hi
I have built AuthStoreModule and ItsmStoreModule with the same plan - from tutorial.
They are both injected in RootStoreModule.
I get "WARNING in Circular dependency detected:" when in auth-store/effects I want to
"this.store.dispatch(new ItsmStoreActions.ItsmBeginLoadingRequests())"

Complete error is
WARNING in Circular dependency detected:
src\app\root-store\root-store.module.ts -> src\app\root-store\auth-store\auth-
store.module.ts -> src\app\root-store\auth-store\effects.ts -> src\app\root-store\index.ts -
> src\app\root-store\root-store.module.ts

Do I have to manage some high level states directly in root-store ?

2 Thread

• Feb 14 '19

Can you show me how you're importing the ItsmStoreActions? Like what does you ts
import statement look like?

1 Thread

• Feb 14 '19

If you can, post a repo on stackblitz or github recreating the issue. Then we can resolve
together. Thanks!!

1 Thread

• Feb 14 '19

I have sent a screenshot with organization of files and the code of ItsmActions import.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The import is only in the itsm-store/index.ts ?

2 Thread

• Feb 14 '19

you may have to directly import the itsm-store/actions.ts to avoid the duplicate/circular
imports. When I get off work I will look into this more.

Thanks for the feedback Daniel! We will get this figured out. And I will add some notes to
the article so that others don't stumble on this issue.

1 Thread

• Feb 14 '19

I can't publish all the code to a github repo, it's too big. I will build a little appli with the
essential of the code.

It's a good idea, so I will test the Store without all the views and the backend.

2 Thread

• Feb 14 '19

Great idea. I always try to recreate things in isolated mini repos.

1 Thread

• Feb 14 '19

I have forked your project and added a second feature store: hoax ;-)
joke-store/effects can only return action of type koke-actions/actions. So I have imported
the main hoax actions and that is running, even if the list of jokes is not proposed.

This is not practical nor clean because I will have about 10-12 main features in the menu
after login (AUTH).
With this technique, I need to import all the initial actions of all features into all feature
actions. 12 * 12 imports ...

English is not my mother language, I hopeyou understand my explainations.

You can see my changes in github.com/dlucazeau/angular-ngrx-...

I will continue to research this important aspect for our application.

2 Thread

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
• Feb 15 '19

I will go from one menu / item to another directly with the router. And the first step after
that will be to activate the feature store. It works well.
As we must be able to install this on-site application with all or only a few features, this
architecture will be suitable.

I will study the lazzy loading of the stores.

Your tutorial was a good introduction to solve my problem.

2 Thread

• Feb 15 '19

I plan on updating my article soon to accommodate for lazy loading feature stores, I will
also address dispatching actions between feature stores

3 Thread

• Feb 15 '19

I will monitor and read the news carefully.

Lazy loading is easy, we have just to move the featureModules from AppModule in their
corresponding module.
In DevRedux we can see a new line with 'update-reducers'

3 Thread

• Feb 15 '19

Agreed.

AWS PROMOTED

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tackle challenges with the AWS security community
Join AWS Security LIVE! for an in-depth look at how to secure your AWS environment. Engage
with security pros, participate in live discussions, and enhance your knowledge.

Learn More

Read next

Wasp: The JavaScript Answer to Django for Web Development


samuel jakshtis - Aug 30

Build An Audio Transcriber and Analyzer using ToolJet and OpenAI 🎙️


Karan Rathod - Aug 29

Faster Pages with React ✨


Florian Rappl - Aug 30

Introduction to Micro-frontend
Ratan Singh - Aug 30

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF

You might also like