Angular app with Ngrx for state management

Reading time: 7 minutes

Description

In this post I will demonstrate how to implement state management in a simple Angular app. NgRx store and NgRx effects will be used.

What is state management

First of all let’s talk about the state. State in simple terms is like a memory of a particular moment in time. Whenever some action happens the state changes. In a software engineering context you can think of a state as various related data that describes a particular moment. We normally store this data in a database. So whenever it is required to perform further actions to the actions that occurred previously to that moment we retrieve that state from the database. In addition to this we can store or keep some state in the application cache. There are various ways you can work with the state. It would depend on the type of application for example web or desktop.

What we will be focusing on this post is state management in the web application. In the present time web applications are becoming more advanced, this means more functionality, faster response time, busier pages etc.

To cope with this extra load of information we can use a state management framework like NgRx. This framework is based on the Redux pattern which is essentially a one way dataflow.

The concept of this pattern is that you replace the state object rather than modify it. This way the state stays immutable. Redux adheres to three principles.

What is required

  • Visual Studio code or any editor of your choice

Let’s create an Angular app

Open VS code and navigate to the root folder of the destination where you want to keep the code for this application.

In VS code new terminal type ‘cd destination-of-your-repo’ for example ‘cd C:\Users\dmitr\source\frontend\countries-ui’

Then type command ‘ng new name-of-your-repo’. In case you created the folder beforehand then you can run a command

ng new name-of-your-repo --directory=./ --skip-install

This would put application files into your folder without creating a nested folder structure.

Type ‘code .’ and it should open another VS code window with application code.

Next identify the folder where you package.json lives. You can run the command ‘dir’ to check if you are in the correct folder.

When you found it then run ‘npm install’

Let’s configure our app to use NgRx

First thing first we need to download it as a npm package. Use this command ‘npm i @ngrx/store’

This package would give us an ability to create a store where we will keep the state. This will be treated as a single source of truth across the application and the functionality that it will cover. The package would also contain code and types for reducer, action and selector. These are part of NgRx state management lifecycle.

Key concepts as described on NgRx website:

  • Actions describe unique events that are dispatched from components and services.
  • State changes are handled by pure functions called reducers that take the current state and the latest action to compute a new state.
  • Selectors are pure functions used to select, derive and compose pieces of state.
  • State is accessed with the Store, an observable of state and an observer of actions.

We would also need the ngrx effects npm package installed. Use this command ‘ npm i @ngrx/effects’

The effects package gives us an ability to correctly handle side effects in NgRx. Some actions in our application can have side effects. These are any effects that happen in the outside world that our application consumes or handles. These could be external devices, api that represent the external state.

When downloading these packages take into account the version of Angular installed on your machine. In other words make sure the version of NgRx you are downloading is compatible with the version of your Angular. To install particular version of npm package the format should be ‘npm install package-name@version-number’

Let’s configure app module to use NgRx

To initialize the store we need to add StoreModule.forRoot({}, {})
We are using forRoot since AppModule is our root application module.

When we use forRoot this means we are defining the main store of the application. Two arguments consist of reducers object and store config.

Then we register root level effects. EffectsModule forRoot takes an array of effects.

I also added ngrx store dev tools, additionally you would need to install a browser plugin. More details here.

There are various options that you can configure for dev tools. The one that you can see in the image is maxAge. This is the number of actions that are allowed to be stored in the history tree of dev tools. In a practice this is the number of state changes that you can replay. In other words you can check retrospectively and investigate what has been affected by one of the actions.

The app concept

The concept of the app will be to have two drop downs. First where you will be able to select a region. Either Asia or Europe. In the second dropdown user will be able to select the actual country. This will display some detailed information about the country.

There would be a component GeographicNavigatorComponent which will have a role of container/smart component. It will host two presenter/dumb components CountryListComponent and RegionListComponent. Also it will host a CountryDetailsComponent component that will only display the data like presenter/dumb component but at the same time will have an access to the store. This is just a basic configuration and you are more than welcome to modify it, so it answers to the best practices.

Additionally there will be a shared wrapper component for dropdown.

Let’s move on to adding some actual code

First of all we will add app state interface. It will represent the structure of our main state object.

It will comprise out of countries and regions object properties each representing their piece of the main state object.

Each piece of the state will have an initial state object. It provides a starting point when the application is first executed and potentially does not have any modified state. To make it predictable we will define the required values explicitly. To make it immutable we will declare it as constant. To have an initial state and declare it as a constant is a normal process. All of this assists in keeping the state in order and being able to quickly detect what happened in case there is an error.

The way we will structure our app in terms of state is we will create a state folder for each section of the state. This is in addition to countries and regions having their own modules.

We will also need to add some models. These would be used to construct our binding model. Also these would be reused in stores and all sorts of data manipulation.

To get our data we will be using an online countries api available at https://restcountries.com which is provided under Mozilla Public License. This means it’s free and open-source software.

And for that we need a service. Where we will make calls. Format and map data as we need it.

Let’s have a look on main components structure of the app

As I mentioned previously there will be a host or parent component called GeographicNavigatorComponent.

It will be a main point of communication with the store. So that whenever the data is updated or refreshed for any of the reasons it will communicate to all other components that are part of this component or chain of components. Respectively if any action that is triggered in the presenter/child component then it should be outputted to the parent, so it can trigger the required action to complete the presenter action.

This is the flow of the container-presentation pattern. This can be handy to divide growing stateful logic into more organized form, so it’s easier to track where problems occur.

To note the important bits of using ngrx. Whenever we want to get a particular piece of state we use selectors. Whenever we need to do something we dispatch an action. Actions can be triggered by the user . They also can be triggered by the external systems actions like api requests and other devices. These actions are then processed in reducers which perform transition from one state to another.

What you need to make sure is so that your component is subscribed to the correct selector. Because whenever some other component dispatches an action and it finishes processing in the reducer that state transition would be reflected in the store. Hence the component with the selector would automatically select this.

Let’s have a look on how things work with ngrx

What we will do is we will review the scenario where all the pieces of ngrx pattern are used.

We will start from countries effects. Effects are they way of communication of our system with external sources.

In our case this effect method would be called by GeographicNavigatorComponent to get a list of countries. When a call to http api is successful we will call a success action. A common approach for a single operation is to have three actions. One is the load action, then success and failure action.

As you can see we call the getCountryListSuccess action with countries as an argument or props method as it’s called according to ngrx docs.

For each action we have an associate state change function in the reducer. In that function we either assign a part of the state or perform some changes to that piece of state. To use the correct words we process the transition of the state rather than change. Since you shouldn’t mutate the state, it should be immutable. The way we can achieve this is by replacing the whole state object. You can see it when we return an object with the state object that we apply spread operator to. And reassign any state object property to the required state. I promise it’s easier that it sounds when you start working with it.

Another point I want to mention to you. The way we configured this application modules ngrx is we have a main app.module that uses StoreModule and EffectsModule forRoot. It’s pretty self explanatory and you might have already seen something similar that Angular uses for routing. It’s the method that is invoked initially when the application loads. It provides the initial configuration with reducers, actions, selectors.

Then there is also forFeature option. This can be used with lazy loading of modules functionality. Also gives you an ability to define its own piece of state. So you can focus on a particular feature of the app and organize your code. This doesn’t create any new store context. It uses the same as the one that was created with forRoot. It loads that piece of state whenever it is required by the application area. As in our example we have a countries feature state. It’s loaded once we activate this part of the application by means of using this component in the GeographicNavigatorComponent template. The example that could give better understanding and will have more justification to use forFeature option is for the case when app redirects from list component to details or form component. The form component doesn’t need to load it’s state without redirection to it by the user’s actions.

Conclusion

The main concept of Ngrx is to have a single source of truth in the format of an object. It is called the store. Basically the store can contain different feature stores or parts that construct the main store object.

The only way to change the store is through dispatching an action. Which is then processed or reduced in the reducer. By having this predefined unidirectional flow it is easier to predict what would happen or what caused a particular transition in the state. Although the correct way of saying it is that action produced these new results. Since when the data was processed in the reducer it effectively created a new state. This way the immutability of the state is supported.

This tutorial demonstrated the beginners level application with standard ngrx functionality. Overall this should be a good start for someone new or it could be a reference point for future developments.

You can find a complete code here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: