Home  >  Angular  >  NgRx

NgRx/Store 4 + Angular 5 Tutorial

By Arvind Rai, November 22, 2017
This page will walk through NgRx/Store 4 and Angular 5 tutorial. @ngrx/store is RxJS powered state management that is inspired by Redux. In NgRx, state is composed of a map of action reducer functions. Reducer functions are called with dispatched action and current or initial state and finally reducer returns immutable state. Find the API of @ngrx/store which we will discuss in our example.

Action: Action is state changes. It describes the fact that something happened but does not specify how the applications state changes.
ActionReducerMap: ActionReducerMap registers the reducers and then it is configured in application module using StoreModule.
ActionReducer: It is used to create reducers such as logger.
MetaReducer: MetaReducer forms the root meta-reducer that is configured in application module using StoreModule.
StoreModule: StoreModule is a module in @ngrx/store API that is used to configure reducers in application module.
createFeatureSelector: It creates a feature selector for a state.
createSelector: It creates a selector to wrap a specific state.
Store: It provides Store.select() and Store.dispatch() to work with reducers. Store.select() selects a selector and Store.dispatch() dispatches the type of action to reducer.

Now find the complete example step by step.

1. Technologies Used

Find the technologies being used in our example.
1. Angular 5.0.0
2. Angular CLI 1.5.0
3. NgRx/Store 4.1.1
4. TypeScript 2.4.2
5. Node.js 6.11.0
6. NPM 3.10.10

2. Install Angular CLI and NgRx/Store

Find the steps to install Angular CLI.
1. Make sure that Node and NPM are installed with minimum version as Node 6.9.x and NPM 3.x.x respectively.
2. Now run command using command prompt.
npm install -g @angular/cli
This will install Angular CLI globally.
3. To generate a new project, run the command.
ng new my-app
4. Now install @ngrx/store. Go to the directory my-app using command prompt and run the command.
npm i @ngrx/store -save
Now we are ready to work with NgRx/Store and Angular.
5. Find the link for above installation references.
For Angular installation: link
For NgRx/Store installation: link

3. Create State

State is a single immutable data structure. We will create state as following.
export interface AppState {
	articleState: ArticleState;
}
export interface ArticleState {
	articles: Article[];
}

4. Create Action Classes

NgRx Action describes state changes. For each and every action, we need to create a class implementing Action and define type and payload where payload is optional. Find the sample code snippet.
export const JAVA = 'Java';
export const  MY_ARTICLES = 'Favorite_Articles';

export class JavaArticlesAction implements Action {
  readonly type = JAVA;
}
export class FavoriteArticlesAction implements Action {
  readonly type = MY_ARTICLES;

  constructor(public payload: Article[]) {}
} 

5. Create Reducer

Reducer describes how the application state changes for any action. We will create reducer as following.
export function reducer(state = initialState, action: fromActions.All): ArticleState {
  switch(action.type) {
    case fromActions.JAVA: {
      return {articles: JAVA_ARTICLES};
    }
    case fromActions.ANGULAR: {
      return {articles: ANGULAR_ARTICLES};
    
    } 
    case fromActions.MY_ARTICLES: {
      return {articles: action.payload};
    }    
    default: {
      return state;
    }
  }	
} 

6. Using createFeatureSelector() and createSelector()

createFeatureSelector() is used to create a feature selector for any specific state.
export const getArticleState = createFeatureSelector<ArticleState>('articleState'); 
createSelector() is used to create selector using feature selector.
export const getArticles = createSelector(
    getArticleState, 
    (state: ArticleState) => state.articles 
); 

7. Using ActionReducerMap

ActionReducerMap registers the reducers. We need to create a constant of ActionReducerMap where we configure our reducers. Then that constant is configured in application module using StoreModule. Find the sample use of ActionReducerMap.
export const reducers: ActionReducerMap<AppState> = {
  articleState: articleReducer.reducer
}; 

8. Using ActionReducer

ActionReducer is used to create reducers such as logger and then it is configured with MetaReducer. Find the sample code for logger reducer.
export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
  return function(state: AppState, action: any): AppState {
    console.log('state', state);
    console.log('action', action);
    return reducer(state, action);
  };
} 
By using above code, for every action we will get console log with state and action name.

9. Using MetaReducer

MetaReducer is formed as an array of ActionReducer that we have created. MetaReducer forms the root meta-reducer that is configured in application module using StoreModule. By default @ngrx/store uses combineReducers to create root meta-reducer. Find the code snippet to create root meta-reducer using MetaReducer.
export const metaReducers: MetaReducer<AppState>[] = !environment.production
  ? [logger]
  : []; 

10. Using StoreModule

StoreModule is a module in @ngrx/store API that is used to configure reducers in application module.
@NgModule({
  imports: [     
        ---
	StoreModule.forRoot(reducers, {metaReducers})
  ]
  ---
}) 

11. Using Store.select() and Store.dispatch()

Store.select() and Store.dispatch() work with reducers to use them. Store.select() selects a selector and Store.dispatch() dispatches the type of action to reducer.
To use Store, create a property of Observable type in component.
articles: Observable<Article[]> 
Now use dependency injection to instantiate Store and select the selector.
constructor(private store: Store<ArticleState>) {
    this.articles = store.select(articleReducer.getArticles);
} 
Now dispatch the action to change the state by reducer.
showJavaArticles(){
    this.store.dispatch(new fromActions.JavaArticlesAction());
} 

12. Complete Example

Find the project structure of our example.
my-app
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--actions
|   |   |    |
|   |   |    |--article.actions.ts
|   |   |
|   |   |--components
|   |   |    |
|   |   |    |--article.component.html
|   |   |    |--article.component.ts
|   |   |
|   |   |--models
|   |   |    |
|   |   |    |--article.ts
|   |   |
|   |   |--reducers
|   |   |    |
|   |   |    |--app.states.ts
|   |   |    |--article.reducer.ts
|   |   |    |--reducers.ts
|   |   |
|   |   |--app.component.ts
|   |   |--app.module.ts 
|   | 
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 
Now find the complete code.
article.actions.ts
import { Action } from '@ngrx/store';
import { Article } from '../models/article';

export const JAVA = 'Java';
export const  ANGULAR = 'Angular';
export const  MY_ARTICLES = 'Favorite_Articles';

export class JavaArticlesAction implements Action {
  readonly type = JAVA;
}

export class AngularArticlesAction implements Action {
  readonly type = ANGULAR;
}

export class FavoriteArticlesAction implements Action {
  readonly type = MY_ARTICLES;

  constructor(public payload: Article[]) {}
}

export type All = JavaArticlesAction | AngularArticlesAction | FavoriteArticlesAction;  
article.ts
export class Article {
    id: number;
    title: string;
    category: string;
}

export const JAVA_ARTICLES: Article[] = [
    {id: 1, title: 'Java Article 1', category: 'Java'},
    {id: 2, title: 'Java Article 2', category: 'Java'},
]
export const ANGULAR_ARTICLES: Article[] = [
    {id: 1, title: 'Angular Article 1', category: 'Angular'},
    {id: 2, title: 'Angular Article 2', category: 'Angular'},
]
export const FAVORITE_ARTICLES: Article[] = [
    {id: 1, title: 'Java Article 1', category: 'Java'},
    {id: 2, title: 'Angular Article 2', category: 'Angular'}
] 
app.states.ts
import { Article } from '../models/article'

export interface AppState {
	articleState: ArticleState;
}

export interface ArticleState {
	articles: Article[];
} 
article.reducer.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromActions from '../actions/article.actions';
import { JAVA_ARTICLES, ANGULAR_ARTICLES } from '../models/article';
import { ArticleState } from './app.states';

export const initialState: ArticleState = { articles: []};

export function reducer(state = initialState, action: fromActions.All): ArticleState {
  switch(action.type) {
    case fromActions.JAVA: {
      return {articles: JAVA_ARTICLES};
    }
    case fromActions.ANGULAR: {
      return {articles: ANGULAR_ARTICLES};
    
    } 
    case fromActions.MY_ARTICLES: {
      return {articles: action.payload};
    }    
    default: {
      return state;
    }
  }	
}

export const getArticleState = createFeatureSelector<ArticleState>('articleState');

export const getArticles = createSelector(
    getArticleState, 
    (state: ArticleState) => state.articles 
); 
reducers.ts
import { ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store';
import { AppState } from './app.states';
import * as articleReducer from './article.reducer';
import { environment } from '../../environments/environment';

export const reducers: ActionReducerMap<AppState> = {
  articleState: articleReducer.reducer
};

export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
  return function(state: AppState, action: any): AppState {
    console.log('state', state);
    console.log('action', action);
    return reducer(state, action);
  };
}

export const metaReducers: MetaReducer<AppState>[] = !environment.production
  ? [logger]
  : []; 
article.component.ts
import { Store } from '@ngrx/store';
import { Component } from '@angular/core';    
import { Observable } from 'rxjs/Observable';
import * as articleReducer from '../reducers/article.reducer';
import * as fromActions from '../actions/article.actions';
import { ArticleState } from '../reducers/app.states';
import { Article, FAVORITE_ARTICLES } from '../models/article';

@Component({
	selector: 'app-article',
	templateUrl: 'article.component.html'
})
export class ArticleComponent {
	articles: Observable<Article[]>

	constructor(private store: Store<ArticleState>) {
		this.articles = store.select(articleReducer.getArticles);
	}
	showJavaArticles(){
		this.store.dispatch(new fromActions.JavaArticlesAction());
	}
	showAngularArticles(){
		this.store.dispatch(new fromActions.AngularArticlesAction());
	}
	showFavoriteArticles(){
		this.store.dispatch(new fromActions.FavoriteArticlesAction(FAVORITE_ARTICLES));
	}
} 
article.component.html
<button (click)="showJavaArticles()">Java Articles</button>
<button (click)="showAngularArticles()">Angular Articles</button>
<button (click)="showFavoriteArticles()">Favorite Articles</button>
<ul>
   <li *ngFor="let article of articles | async">
        {{article.id}} - {{article.title}} - {{article.category}}   
   </li>
</ul> 
app.component.ts
import { Component } from '@angular/core';

@Component({
   selector: 'app-root',
   template: `
	<app-article></app-article>
     `
})
export class AppComponent {
} 
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { AppComponent }  from './app.component';
import { ArticleComponent }  from './components/article.component';
import { reducers, metaReducers } from './reducers/reducers';

@NgModule({
  imports: [     
        BrowserModule,
	StoreModule.forRoot(reducers, {metaReducers})
  ],
  declarations: [
        AppComponent,
	ArticleComponent
  ],
  providers: [

  ],
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 

13. Run Application

To run the application, find following steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application.
3. Run ng serve using command prompt.
4. Now access the URL http://localhost:4200
Find the print screen of output.
NgRx/Store 4 + Angular 5 Tutorial

14. References

@ngrx/store
Angular QuickStart

15. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
FIND MORE TUTORILAS


Copyright ©2017 concretepage.com, all rights reserved |Privacy Policy | Contact Us