In these Angular NgRX story series I am going to cover specific parts of the Angular application which are not obvious to implement with the NgRx library.
NgRx is RxJS powered state management for Angular applications, inspired by Redux. It helps us to manage a state of the application, and there are some benefits over the standard service-oriented approach.
If you are lazy to read a step-by-step explanation, check Github repo example for the full implementation.
We are writing an Angular app with the NgRX approach. We want to be sure that before we can enter to Profile page we have data to show in this component. You can check the standard implementation of the Route Resolver from official docs. And we will go forward to understand how to achieve that with the NgRX State management.
Let’s take a look at
ProfileComponent. It’s a classical subscription to Observable been returned by network call from
ApiService. No matter what’s for
ProfileComponent is using this data, let’s assume we need to be sure data exists before we enter the component.
Defining Reducer and Actions
My personal preferences to keep files in feature oriented structure. So we will place new files to the same folder as our
ProfileComponent were placed. Here is a new structure of folder after we’ve created
Profile folder structure
Taking a look at
IProfileActions— list of actions which we will use to manipulate data related to Profile entity. In case want to extend functionality, we can add such actions as
PROFILE_CHANGEetc. For now, we are fine with action which will trigger for updating of the Profile data.
UpdateAction— is single action type which we define to describe an action of getting new data (
Now let's handle this action in the
IProfileState— to describe how are we going to extend basic reducer of application.
initialState— default value for
reducer— processes single action (UPDATE) to describe state changes after the action was triggered with some payload
Extending root reducer
We compose root state of the application by adding reducer Object to all
reducers (LOC: 11
And the last thing — we defining the StoreModule with the reducers we’ve created before
storeReducers (LOC: 8
Setting up route resolver
Now, let’s put resolver on the route we want to wait until data will be loaded.
Continue by creating
profile.resolver.ts with the corresponding class as we name it on the LOC: 8 in
app.routes.ts above. This line is saying to wait for navigation to
/profile route until data will be resolved in the
ProfileResolver which we are going to implement in a second.
A newly created class implementing
Resolve interface which obliges us to define
resolve function. This function can return Promise, Observable or expected type directly.
LOC 23: Subscribe store to listen for
profile object changes.
LOC 24: Map values to get the only
profileData from the general
LOC 25: Ignore all triggers where
profileData does not exist
LOC 26: take(1) — getting only first value, as long as we need single one to resolve route
Getting the first value of
profile object located in the store. If there is no value:
- Get data by using
- Convert it to Promise (we need only once, so no point for subscribing to Observable)
- And dispatch
datawhich we just got from server
Route resolver conclusion
We’ve initialized data by getting it from the server, and dispatching new state for the application store. Meanwhile, we told our resolve function to listen for the store changes, and return Observable with the first nonempty
ProfileData it will find in the store.
Using route data in component
Finally, we are getting
profileData from route snapshot, assuming it must exist there.
NgRx driven applications make very clear a data flow process through the application. I hope that will give you some patterns how to solve route resolving problem.
Here are few things you can also do to improve your application experience:
initProfileData— could be moved or removed if you have some different places to init data.
- You can implement spinner to show while route resolvers are in action. There is some basic implementation in the full repo example.
- Your component could be subscribed to store directly if you expect data to be changed from another place. In such case, you can convert
ProfileResolverto return Boolean instead of data. Could be nice homework exercise to master this topic 😜.
Thanks for reading! If you want me to write more similar stories recommend this post (by clicking the ❤ button).
Write your thoughts to comments and subscribe to my medium to find more stories.