I’ve been developing Angular 4 applications for some time now. For state management — Services and components —I make do with these. But as the application grows in size and complexity — State management becomes lot harder. So It’s time to look elsewhere for answers.
Their core idea is simple —
- We can’t mutate the application state. State is immutable.
- State mutation can only be done by dispatching Actions.
- Only pure Reducer functions are capable of changing Application State.
In case you’re not familiar with “Pure Functions” — They are the kind of functions that will always return the same values for the same arguments. So If we adopt a functional programming style — We get more preciseness and control over our Application State. Debugging becomes much more friendlier. By restricting the ways the state can be changed — We reduce the chances bugs can appear.
We are going to create a very basic Todo App to demonstrate the basic structure of an NgRx app. I am going to use the API backend that I made in the MEAN App tutorial (Part 1). For the Angular side — We will be converting the Todo App of this tutorial — MEAN App tutorial(Part 2) into a NgRx version.
ng new todoapp-angular-ngrx --style=scss to create the basic Angular App. And setup Font-awesome and Bootstrap by following the MEAN tutorial Part 2.
npm install --save @ngrx/store @ngrx/effects to install NgRx Store and Effects in the application.
The official NgRx example app contains a different directory structure than the CLI recommended one. I’ll try to maintain the CLI structure here. It will be a familiar to the developers.
| |- todo
| |- todo-list/ _______________ # TodoList component
| |- todo-list-item/_______________ # TodoListItem component
| |- todo.model.ts
| |- todo.service.ts
| |- todo/
| |- todo.action.ts
| |- todo.effects.ts
| |- todo.reducers.ts
| |- todo.state.ts
We need to create the Todo Model.
We’ve also created a static function that will return us a Mock Todo when we need it.
We now create a some state models in
I’ve thrown in bunch of state properties that we probably won’t need for this example. Later We can add states any time to work with the reducer functions.
First we need to create actions. Actions define the changes of the Data.
- Inside the
src/appdirectory create a folder store — inside that a folder todo. Create a file
At first, we are creating the string const variables like this —
export const GET_TODO = ‘[Todo] GET_TODO’ . We are exporting this const, because we will need these in the reducer and effects later. We will use this const string to define the type of the actions.
Action classes are created with the
readonly type property. Here we create constructors for the Actions that will define the changes to the state. Right now the
GET_TODO actions are not used in this example app, only the
GET_TODOS actions will be used.
Now we need to create the reducer functions that will take the state and action and will return a new state. It can take the payload of the action and can commit change in the new state.
The TodoListState is the main state of the app. It contains the todosListState array and loading and pending properties.
We will define a defaultTodoStates that will take the Mock Todo Model and Todo State to return a TodoState default instance.
And the defaultState is the state that the reducer function takes by default. And this reducer function contains the switch statement which will filter out the dispatched actions and will return the new state.
This is the first case. If the type of the action is GET_TODOS it will return a new state that has loading property true. This action doesn’t handle the returned state from the Server. This action just returns the default state at first call.
GET_TODOS_SUCCESS action takes the returned state from the server and set loading to false.
In NgRx, to listen for these actions and execute new actions based on them — is handled by effects.
To use effects you need to install
npm install --save @ngrx/effects and provide them in the Module.
This is the
TodoEffects Class. It will contain all the necessary side effects.
This is the first side effect. If an action of Type
GetTodo is dispatched this sideffect
GeTodo$ will be executed.
It first send a Get request to get the
todo list with Pagination data. If the request is successful then Then this side effect parse the response into a
TodoState List object and then pass that in the payload of
If an error occurs during the process an Action of type
GetTodoError is dispatched. For this example we are not handling the Error Effects or actions.
We need to import the StoreModule and the EffectsModule into AppModule to use the NgRx store and effects.
Now we need to create the components needed to display the todo lists.
ng g c components/todo/todo-list To create the Todo List component.
AppState intefact has a property todos of type TodoListState which will be used for the Store to be injected into the component.
this.todoListState$ = this.store.select(state => state.todos); To select the todoListState from the Store.
ng g c components/todo/todo-list-item To generate TodoListItem Component.
We change the ChangeDetectionStrategy to OnPush for better performance. Now the components Change Detection will run only when the input values will change.
Now we need to create Input and Output variables to communicate with it’s parent component.
This child component wont dispatch any actions. This is called a Dumb Component. It will only output the values to It’s parent -The TodoListComponent. Only the TodoListComponent will have the knowledge of the store and will dispatch actions.
This is the Todo List Item. We will add class to the list item based on the state of the specific Todo.
This is the accompanying SCSS file.
If the Todo is being edited or created, we will show different controls.
These are the buttons that will emit different events like Edit, Complete and Delete.
And these are the methods that emits these values to the parent component.
We now catch the output events in the
TodoListComponent. We will now dispatch the Actions from there.
Here we’ve used the Actions that we are going to create now. Important portion of this action construction is that for every kind of Action there are two extra *success and *error Actions for handling backend response.
This is the full Todo Actions file.
Todo Action Reducer
Here we are using the
… spread operator of ES6 to spread the state object. Here the state object is expanded and the later properties will overwrite the previous ones.
Then in the todos array we find the created todo and then set the loading property to be true.
Get Todos Action
These are the actions for getting Todo List.
Delete Todo Action
Here we splice the todo from the todos array when the action is first dispatched. If It’s successful nothing is done, But If it’s an error then, The todo will be appended to the list again.
Update Todo Action
The first Update Action will only set the loading property to be true. After the Update Todo Effects handles the server request and then dispatches the UpdateTodoSuccess Action the loading is set to false.
So for a brief amount of time, the loading class will be activated in the todo list item.
For modifying the todoState inside the todoListState, this
modifyTodoState function was separated from the main reducer function.
Complete Todo Action
And the last action will set the status of the Todo to be “done”. But the great thing is that we re use the update success and error Actions for the total state management for the action.
Now we can run our app to see the final result —
This example app has more or less the same small features I did on this — MEAN App tutorial with Angular 4 Tutorial. But redoing this in NgRx took a ton more code.
We have to write a lot of code in order to make the states immutable and perform mutation by this exact system. Clearly an application as simple as this won’t be the right candidate for the Redux pattern.
This is the Github Repository- https://github.com/nomanHasan/todoapp-angular-ngrx for this example app.