Source
https://github.com/jarodms/angular-nodejs-bootstrap-ngrx
[UPDATE 04/09/2020 – The master branch was updated to use the createAction factory; the branch action-class uses classes for creating actions.]
Resources
https://angular.io/
https://ngrx.io/docs
Background
I had previously built a standalone app that featured a “JSON Server”, NodeJS, and Angular front-end that would store your “ToDo” items in a local db.json file. This was a rather simple example of using API’s in an Angular App without the real need to have a full-fledged backend server. With one simple command you could run everything to try it out:
npm run start-dev
The source for this basic app: https://github.com/jarodms/angular-nodejs-bootstrap
This was nice and simple, but I wanted to use some of the things that I had recently learned with NgRx and apply it to this app. So I built a new version that is pretty much the same except that it uses NgRx: https://github.com/jarodms/angular-nodejs-bootstrap-ngrx
NgRx
This framework had been taboo to me for a while. I had used it a while back and never really took the time to understand it as much as I should have. But recently I took it upon myself to “better understand rather than just use”. That’s what we programmers do right? Learn, implement, learn some more.
In the world of NgRx and state management, a very light and simple “ToDo” app might not be the best place to use NgRx. NgRx has always seemed to be for larger apps. But that is the reason I wanted to build this example app, because it is so simple and can make for understanding NgRx even easier. And…you never know when your simple app will become much, much larger.
Commands
Here are some basic commands I used throughout building this app. Note that some are dev dependencies and some are not.
npm install @ngrx/schematics --save-dev
Scaffolding library for Angular applications. It helps us generate files using the Angular CLI.
npm install @ngrx/store-devtools --save-dev
Dev Tools for debugging NgRx apps. You will still need an extension for viewing the data in your browser. Here’s one for Chrome: https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd
and one for Firefox: https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/
npm install @ngrx/store
npm install @ngrx/effects
* other dependencies
Steps
- Setup the Angular app to use NgRx
ng generate @ngrx/schematics:store State --root --module app.module.ts
- Add a Reducer
ng g @ngrx/schematics:reducer Todos --group
- Add an Action
ng g @ngrx/schematics:action Todo --group
- Add an Effect
ng g @ngrx/schematics:effect Todo --group --root
For more info on the above commands, see the NgRx documentation: https://ngrx.io/guide/schematics
Result
Once all the code was added, you can see the result of what we need to do for the main ToDo component of todos.component.ts located in src/app/todos/todos.component.ts
Notice that I am no longer injecting TodosService in the constructor as I was in the non-NgRx example. The constructor is subscribing to the store for changes. When I need to add, delete, or complete a Todo, I am not calling a service directly. Rather, I am dispatching an Action. The side-effect (Effect) of dispatching these actions will be to update the state, and therefore my component will receive updated data.
Larger App
So with this example, I was able to immediately apply it to what will soon be a larger mobile app using Angular and Ionic.
The Scenario: What if you needed to get the location of the user, and based on that location retrieve data using an API that will show location-based content to the user.
In the past, before NgRx, I would do something like this:
this.geolocation
.getCurrentPosition().then(resp => {
this.location = resp.coords;
// Call the web service that uses the location to get content
this.updateShows();
// Now update my stats server
// Now call this API to do something else
// Do something else too....
})
.catch(error => {
});
this.geolocation.getCurrentPosition()
.then(resp => {
this.location = resp.coords;
this.store.dispatch(new UpdateLocation());
})
.catch(error => {
});
constructor(private store: Store<State>) {
store.select(state => state.shows).subscribe(shows => this.allShows = shows.shows); }