Angular: an example of using NGRX

image
This article is a continuation of the post “Angular: A Comprehensive Introduction to NGRX.”

NGRX Example


In our example, there will be a list of users, a user details page, and some initial configuration information that you should get when you start the application. We are going to implement some important NGRX threads.

Action plan:

  1. Library installation
  2. Creating a folder structure for storage
  3. Creation of storage and initial values
  4. Creating Actions
  5. Creating Gearboxes (Reducers)
  6. Creating Effects
  7. Creating Selectors
  8. Final setup
  9. Using storage in components

So let's do it ...

Library installation


We are going to use Angle Cli to create the project, and then we will add the ngrx libraries.

Create a project:

ng new angular-ngrx --style=scss

Add the necessary NGRX libraries:

npm install @ngrx/core @ngrx/store @ngrx/effects @ngrx/store-devtools @ngrx/router-store --save

We install almost all the ngrx ecosystem libraries. Most of them describe their purpose quite clearly: the kernel, storage, effects, but there is a couple for which you may wonder why they are needed?

  • store-devtools - A powerful debugging tool.
  • router-store - Saves the state of the angular router in storage.

Folder structure for storage


Let's start by discussing the file structure of the repository. This file structure and the entire storage configuration must exist in the main module of your application, but in our example it is absent, therefore the storage will exist in the main module of our application (the steps are almost the same if you do this in the main module).

image

The folder structure is a representation of the actual storage composition. You will have a main folder with the name “store” and five subfolders that represent each of the key players in the store: “Actions”, “Effects”, “Redurs”, “Selectors” and “State”.

Creation of storage and initial values


As we mentioned earlier, we will have two main sections: our application, users, and configuration. For both of them, we need to create a state and an initial state, and also do the same for the state of the application.

We created two interfaces for user definition and configuration. We also have one for the user's HTTP response, it's just an IUser array.

image

image

Let's start with the user state (store / state / user.state.ts):

image

What are we doing here:

  1. We create and export an interface with a user environment structure.
  2. We do the same with the initial user state, which implements the newly created interface.

For the configuration state, we do the same (store / states / config.state.ts):

image

Finally, we need to generate the application state (store / states / app.state.ts):

image

  1. The application state contains the user state and configuration, as well as the state of the router.
  2. Then we set the initial state of the application.
  3. Finally, it exports the function to get the initial state (we will use it later).

Creating Actions


Be sure to read the definition of action that we discussed in the previous article .
We need to create actions for users and settings. Let's start with the user actions (store / actions / user.actions.ts):

image

Let's walk through the code a bit:

  1. Enum, . , , , .
  2. . Action ngrx. , , , .
  3. , , . , , , .

And that’s all ... creating actions is easy. Let's see what the configuration actions look like (store / actions / config.actions.ts):

image

Nothing new here, you probably now feel comfortable with everything that happens in this file.

Great, we have already determined the status and actions ... let's create gears!

Creating Gearboxes


Be sure to read the definition of gearboxes that we discussed in a previous article .
We will have gearboxes that respond to some actions, because others will be handled by effects that we are going to implement later.

We will need a reducer for users and another for configuration, but we will also need to generate application reducers, let's start by looking at user reducers (store / redurs / user.reducers.ts):

image

Let's discuss the implementation:

  1. A reducer declaration receives a state and, in this case, a user action and returns an IUserState.
  2. Using the switch statement, we generate observations for each possible type of action.
  3. Each case returns a new object, which is the result of the merger of the old state and the new value.
  4. All gearboxes have a default result, which simply returns state without any changes.

And it's all. You will not find anything else in the gearbox. Let's take a look at the configuration reducers (state / redurs / config.reducers.ts):

image

Finally, let's look at the application reducers (store):

image

Here we add all the reducers to the application’s reducer map. We use an action reducer map for additional type checking. Later we are going to provide this application with reducers for the store module.

Add Effects


Be sure to read the definition of “Effects,” which we discussed in a previous article .
You probably already noticed that in gearboxes we do not process all actions. This is because we are going to handle missed actions in effects, because these actions have side effects.

Let's start with custom effects (store / effects / user.effects.ts):

image

A lot of what happens in this file. Let's try to explain them:

  1. We declare our custom effects using an inject decorator.
  2. We declare our effects using the effects decorator provided by ngrx / Effects.
  3. Using the actions provided by ngrx / Effects, we are going to start the pipeline of our operator for this effect.
  4. The next step is to set the type of effect action using the ofType operator.
  5. The following parts are the rxjs operators that we use to get what we need (we already have a link to the rxjs documentation in this article).
  6. Finally, in the last statement, Effect will send another action.
  7. In the constructor, we implement the services that we are going to use, the actions for ngrx / Effects, and in this case also the repository (note that this is a demo, and we get the selected user from the list of users in our repository).

This is almost the same structure that you will see with any effect. In this case, we send only a successful action, but we can send errors or any other state that we want to process in our application reducers.

Consider the configuration effects (store / effects / config.effects.ts):

image

Now it's time to talk about selectors ...


, .
It makes no sense to repeat the slices of our state everywhere, so let's create some selectors that we can reuse.

As always, let's first look at user selectors (store / selectors / user.selectors.ts):

image

This is really understandable because we do not do any data transformations in our selectors, but simply return the slice of the store referenced by the selector using the function createSelector from ngrx / store.

The first parameter is a fragment of the storage that will be used to receive data (it can be an array with several parts of the state), the second parameter is an anonymous function that will decide what the selector will return.

Let's look at the configuration selectors (store / selectors / config.selectors.ts):

image

Final setup


Well, we have created everything that our storage needs, but we still lack one thing - to put everything together. I am going to do this in the application module, but you can apply the same in the main module of your application.

Let's add an application module:

image

What have we done here:

  1. We import our gearboxes and provide them in the forRoot storage module.
  2. We import our effects and provide them inside the array in the forRoot effects module.
  3. We set up the configuration for the state module of the router that provides the stateKey router.
  4. And we add store developer tools if the environment is not production.

The first two steps are necessary, while steps 3 and 4 I highly recommend, but they are not required.

Now we finally done ... we can use our storage in components!

Storage usage in some components


We are nearing the end! Let's see how to use our storage ...

First, let's get the configuration when the application starts:

image

  1. We are adding storage to our app.component.
  2. We set the component property to the value of the selector in the configuration, because we want to display some of this information in HTML.
  3. At onInit, we submit the action to get the configuration.

That's all ... We are already coding an effect that will control this action, and a reducer that will handle this effect. As soon as the Store enters a new state, the selector will change the value of our property.

How to associate this with HTML:

image

As soon as config $ has a value, we will see it in HTML.

Now let's see the list of users (containers / users / users.component.ts):

image

  1. Just as we manage the configuration, we are going to get a list of users. First, we inject the repository into the user component.
  2. On onInit, we post an action to get users.
  3. We create a property for the component and assign it a list of users using the user list selector.

HTML looks like this:

image

We display the list of users in the presentation component, sending the list and associating the selected user with the navigation function that can be seen in the user container component above.

Let's look at the custom container component:

image

This component receives the parameter identifier from the activated route, and you are probably already familiar with the rest, sends the identifier as the parameter, selects the selected user ...

If you are debugging the application, you can see the developer tools, which are quite simple to use ... but in this article we have studied enough, and I hope that you can easily figure out these tools.

Conclusion


In this article, I tried to provide a clear and understandable introduction to NGRX, giving you everything you need to know to start development using this tool.

I really hope that the article will help you understand the action template and recommend that you download it and play a little with the code.

All Articles