The Evolution of Angular State Management
State management within Angular started out as a single celled organism if you will in the form of a single controller managing all the state for the application. If this is a single page application, one controller makes sense right? We emerged out of the ice age by starting to group our views and controllers into smaller, self-contained units either within a directive or a route. This was a vast improvement, but there was still the problem of managing complex state within our applications. It was not uncommon for us to have bits and pieces of state strewn across our application tucked inside of controllers, services, routes, directives, and occasionally, in our templates. Mutable state in itself is not inherently evil but shared mutable state is a recipe for disaster.
Just as modern web frameworks like Angular permanently altered our jQuery-centric approach to app development, React has fundamentally changed the way that we approach state-management while using modern web frameworks. Redux is front and center of this shift as it introduced an elegant, yet profoundly simple way to manage application state. It is worth mentioning, Redux (big R) is a library but more importantly it is a design pattern (little r) that is completely framework agnostic and coincidentally works really well with Angular.
Required Viewing
This entire post was inspired by the amazing Egghead.io – Getting Started with Redux series by Dan Abramov. There is no better way to learn Redux than by having its creator explain it to you. It is 100% free and has changed the way that I approach programming in general.
The beauty of redux is that it can be articulated in just a few sentences. In fact, my “ah ha!” moment could be summarized in three main points.
Single State Tree
The fundamental premise of redux is that the entire state of the application is represented in a single JavaScript object called a store, or application store, that can be acted upon using special functions called reducers. Equally important is that state is immutable and reducers are the only part of the application that can change them. As you can see in the graphic above, the store is the center of the appliation universe.
The consolidation and immutability of state makes understanding and predicting how an application will behave exponentially easier.
Events Flow Up
In redux, user events are captured and emitted up to a reducer for processing. In Angular 1.x, it was a very common anti-pattern to see bloated controllers with large chunks of logic dedicated to manage local state. By moving logic that can directly manipulate state to reducers, the burden placed upon our components become negligible. In Angular 2, you will often see dumb controllers who do nothing more than capture an event and emit it via output to its parent controller.
In the graphic above, you will see two events flows. One is an event being emitted from a child component to its parent component and then onto the reducer. The second flow is an event being emitted to a service to perform an asynchronous operation and then the result of that being emitted into the reducer. All roads lead to the reducer.
State Flows Down
While events flow up, state flows down from the parent component to its children components. Angular 2 makes this really easy by declaring input on a child component for the parent component to pass state to it. This has some serious implications in terms of change detection which we will get into in a bit.
@ngrx/store
The icing on the cake is that state moving through an Angular 2 application is exponentially easier with the introduction of observables and the async pipe. My buddy Rob Wormald created an awesome Redux implementation using RxJS called @ngrx/store. This gives us all the power of Redux combined with the power of observables which makes for a very, very powerful stack.
The Sample Application
We are going to be building out a simple master-detail REST application that lists a collection of items and then we can select an item and edit it or create a new item. To illustrate how @ngrx/store works with asynchronous operations, we are going to use json-server to provide use a REST API for use to consume with the Angular 2 http service. If you want to see a simplified version of the application, you can check out the simple-data-flow branch to skip the HTTP calls.
Grab the code and let’s get started!
CodeLaying the Foundation
We are going to cover a lot of ground over the course of this lesson, and so we will do our best to take baby steps along the way. There is always the initial phase of a new concept where you have to lay some groundwork before you can start to expound on specific components. In this section, we are going to build out just enough Angular to give us the space to start talking about redux and ngrx in the context of a working application. Don’t get too hung up on the particulars just yet as we will revisit everything more than once to fortify the ideas we are covering.
Reducers Take One
To facilitate our master-detail interface, we need to manage an array of items as well as the currently selected item. We will use @ngrx/store to provide use with a store for us to well… store our state.
To manage our application state, we need to kick things off by creating our items and selectedItem reducers. A traditional reducer is nothing more than a function that takes a state object and an action to perform. Our ngrx reducer is slightly different in that the second parameter is an object with the type of action to perform and the payload for that action. We can also set the default value for the state to ensure that everything initializes smoothly.
// The "items" reducer performs actions on our list of items export const items = (state: any = [], {type, payload}) => { switch (type) { default: return state; } };
We will build our reducers out to handle specific actions but for now, we are going to set the default state of the switch statement to just return state. The code snippet above and below are almost identical except that one is the items reducer and the other is the selectedItem reducer. Seeing them side by side makes it easier to identify the underlying pattern when creating reducers.
// The "selectedItem" reducer handles the currently selected item export const selectedItem = (state: any = null, {type, payload}) => { switch (type) { default: return state; } };
Making an interface for the application store really helped me to understand how reducers fit into the application. In our AppStore interface, you can see that we are dealing with a single object that has an items collection and a selectedItem property which holds a single Item object.
export interface AppStore { items: Item[]; selectedItem: Item; }
If we needed to add additional functionality, the store would just expand with new key value pairs to accommodate the updated model.
Inject the Store
Now that our reducers have been defined, we need to make them available by adding them to our application store and then injecting that into our application. The first step is to import items, selectedItem and provideStore into our application. The provideStore is appropriately named in that it provides us with an application store to use for the life-cycle of the application.
We initialize our store by calling provideStore and passing in an object that contains our items and selectedItem reducers. Notice that we are passing in an object that matches our AppStore interface.
We then make the store available to our entire application by defining it as an application dependency when we call bootstrap to initialize our application.
import {bootstrap} from 'angular2/platform/browser'; import {App} from './src/app'; import {provideStore} from '@ngrx/store'; import {ItemsService, items, selectedItem} from './src/items'; bootstrap(App, [ ItemsService, // The actions that consume our store provideStore({items, selectedItem}) // The store that defines our app state ]) .catch(err => console.error(err));
You may have noticed that we are also importing and injecting ItemsService; we will define it next as it will be the primary consumer of our newly minted store.
Create the Items Service
The first and simplest iteration of our ItemsService will expose the items collection by pulling it from the store. Notice that we have typed our items collection as an Observable that contains an Array of Item objects. The benefits of having our array wrapped in an observable will become clearer once we start to consume the collection in our components. We are also injecting our store into our constructor and typing it to the AppStore interface that we declared earlier.
@Injectable() export class ItemsService { items: Observable<Array<Item>>; constructor(private store: Store<AppStore>) { this.items = store.select('items'); // Bind an observable of our items to "ItemsService" } }
Because we are basically dealing with a key-value store, we can set this.items by calling store.select(items’). The select method returns an observable with our collection in it.
Important! The reason that I have created a service to pull the items collection from the store is because we are going to introduce asynchronous operations when we start to wire everything up to talk to our remote API. This abstraction allows us to accommodate some potentially complex async operations before handing everything off to the reducer for processing.
Consume the Items
Now that we have created the ItemsService that has an items collection available, we will consume it in our App component. Just like in the ItemsService, we will declare our items collection as items: Observable<Array<Item>>. While we are at it, we will also define our selectedItem as an observable that contains a single Item object.
export class App { items: Observable<Array<Item>>; selectedItem: Observable<Item>; constructor(private itemsService: ItemsService, private store: Store<AppStore>) { this.items = itemsService.items; // Bind to the "items" observable on the "ItemsService" this.selectedItem = store.select('selectedItem'); // Bind the "selectedItem" observable from the store } }
To set this.items, we will assign it the items collection from the ItemsService and to set this.selectedItem, we will fetch it directly from our store by calling store.select(‘selectedItem’). If you recall, I created the ItemsService to abstract our asynchronous operations when dealing with the items collection. Managing selectedItem is completely synchronous in nature and so I could not justify creating a SelectedItemService for it. This is why I am using ItemsService when dealing with items but deal with the store directly when getting selectedItem state. You could create a service to handle this for the sake of symmetry and that would be completely justified.
Display the Items
Angular 2 is designed for the creation and composition of small specific components. Our application has two sub-components called ItemsList and ItemDetail which are responsible for listing all items and displaying the details of the selected item, respectively.
@Component({ selector: 'my-app', providers: [], template: HTML_TEMPLATE, directives: [ItemList, ItemDetail], changeDetection: ChangeDetectionStrategy.OnPush })
My syntax highlighter does not handle inline templates very well which is why I have separated them out into two separate code blocks. In practice, I recommend keeping your components fine-grained enough that it is easy to inline your templates. A really large HTML snippet probably means that you are doing too much.
In the my-app template, we are initializing the items-list component with a property binding to items that is bound to our local items collection. This is similar to isolated scope in Angular 1.x in that we are creating a defined input on the child component with [items] and then binding it to the value of whatever the items collection happens to be on the parent component. Because we are dealing with observables, we can bypass a lot of boilerplate by using the async pipe to pass the updated values directly to our inputs without having to extract them. By “boilerplate”, I am referring to a common Angular 1.x scenario where we could call a service in our controller and when the promise resolved, we would take the results and assign it to a property we were binding to. In Angular 2, we skip this step entirely by allowing the pipe to assign the asynchronous to our template for us.
<div> <items-list [items]="items | async"></items-list> </div> <div> <item-detail [item]="selectedItem | async"></item-detail> </div>
We will follow the same pattern for the selectedItem by passing it to the item-detail component as item. We have now laid the foundation for our application, and now the stage is set for us to dig into the three main features of data flow in a redux application.
Centralized State
To reiterate, the single most important concept within redux is that the entire state of your application is centralized into a single JavaScript object tree. This is, in my opinion, the largest shift from how we used to write Angular applications to where we are now. We manage our application state through a reducer function which takes the original state and an action, performs a unit of logic based on the particular action and returns a new state object. We will build out our children components to display items and selectedItem and keep an eye out for the fact that they are being populated by this master, single state tree.
Our reducers are the only thing that can modify application state and so we will start out with the selectedItem reducer since it is by far the simplest of the two we have in our application. When an event is dispatched from the store with an action type of SELECT_ITEM, it will hit the first condition in the switch statement and return the payload as the new state. In plain English, we are telling our reducer to “take this new item and assign it as the currently selected item.” Also, actions are customarily strings that are all in caps and often times defined as application constants.
export const selectedItem = (state: any = null, {type, payload}) => { switch (type) { case 'SELECT_ITEM': return payload; default: return state; } };
Because our object state tree is read-only, our response for every action must return a new state object without mutating the previous state object. Enforcing immutability in our reducers is critical when implementing redux and so will step through each action below and discuss how we can accomplish this.
export const items = (state: any = [], {type, payload}) => { switch (type) { case 'ADD_ITEMS': return payload; case 'CREATE_ITEM': return [...state, payload]; case 'UPDATE_ITEM': return state.map(item => { return item.id === payload.id ? Object.assign({}, item, payload) : item; }); case 'DELETE_ITEM': return state.filter(item => { return item.id !== payload.id; }); default: return state; } };
ADD_ITEMS returns whatever collection we send in as the new array.
CREATE_ITEM returns a new array by concatenating the existing items array with our new item.
UPDATE_ITEM returns a new array by mapping through the current array, finding the item we want to update and cloning a new object using Object.assign.
DELETE_ITEM returns a new array by filtering out the item that we want to delete.
By centralizing our state into a single state tree and then grouping the code that operates on our tree into reducers makes our application so much easier to reason about. Another benefit is that by having our logic segmented into pure units within our reducer, this makes testing our application very easy.
State Down Interlude
Just to give a preview of how this data flow is connected, let us take a look at our ItemsService and see how we can initiate an action on our items reducer. We will eventually replace the loadItems method with an HTTP call but for now, we will just assume that we are hard coding some sample items and assigning it to the initialItems array. To execute an action, we will call this.store.dispatch and pass in our actions object with a type of ADD_ITEMS and a payload of initialItems.
@Injectable() export class ItemsService { items:Observable <Array<Item>>; constructor(private store:Store<AppStore>) { this.items = store.select('items'); } loadItems() { let initialItems:Item[] = [ // ITEM OBJECTS HERE ]; this.store.dispatch({type: 'ADD_ITEMS', payload: initialItems}); } }
The interesting thing is that every time we dispatch the ADD_ITEMS event, our local items collection is automatically updated because it is being via an observable. Because we are consuming items in our App component, it is automatically updated there as well. And if we were (and we are) passing that collection to the ItemsList component, it would automatically update at the subcomponent level as well.
Redux is a great pattern for centralizing state management with immutable data structures. Add in observables and you now have an ultra-convenient way to move that state down through the application by binding directly to the stream of values provided by the observable.
State Down
Another cornerstone of redux is that state always flows down. To illustrate this point, we will start with the App component and follow our items and selectedItem data down to the child components. We are populating items from the ItemsService (because it will eventually be an asynchronous operation) and pulling in selectedItem directly from the store.
export class App { items: Observable<Array<Item>>; selectedItem: Observable<Item>; constructor(private itemsService: ItemsService, private store: Store<AppStore>) { this.items = itemsService.items; this.selectedItem = store.select('selectedItem'); this.selectedItem.subscribe(v => console.log(v)); itemsService.loadItems(); // "itemsService.loadItems" dispatches the "ADD_ITEMS" event to our store, } // which in turn updates the "items" collection }
This is the only place in the entire application where those two properties are set. We will learn in a moment how to do some sleight of hand when we get to the ItemDetails component to localize state mutation but we never directly manipulate these values again. Conceptually, this is a huge shift from how we have approached Angular applications, and the implications are huge. We no longer need change detection if we are not directly mutating data within our component.
The App component takes items and selectedItem and hands them off via property bindings to its child components.
<div> <items-list [items]="items | async"></items-list> </div> <div> <item-detail [item]="selectedItem | async"></item-detail> </div>
In our ItemsList component, we pick up the items collection by annotating our local items property with @Input().
@Component({ selector: 'items-list', template: HTML_TEMPLATE }) class ItemList { @Input() items: Item[]; }
And in the HTML template, we display it in the view using ngFor to loop over items and create a template for each one.
<div *ngFor="#item of items"> <div> <h2>{{item.name}}</h2> </div> <div> {{item.description}} </div> </div>
The pattern is slightly more complicated in our ItemDetail component because we need to allow a user to create a new item or edit an existing item. You are going to ask the same question I did as I was learning redux. How can you edit an existing item without mutating it? Behold the sleight of hand! We will create a local copy of our item to work with so that we are not directly mutating our selected item. This has the added benefit of allowing us to cancel the operation without any side-effects.
To accomplish this, we will modify our annotation slightly to assign our item input to a locally scoped _item property using @Input(‘item’) _item: Item;. We can then leverage the power of ES6 and create a setter for _item and perform addtional logic everytime the object is updated. In our case, we are going to create a copy of _item using Object.assign and assign it to this.selectedItem which we will use to bind our form to. We are also going to create a property and store the name of the original item so that user is aware of what item they are currently working with. This is strictly motivated by the user experience but these little things make a big difference.
@Component({ selector: 'item-detail', template: HTML_TEMPLATE }) class ItemDetail { @Input('item') _item: Item; originalName: string; selectedItem: Item; // Every time the "item" input is changed, we copy it locally (and keep the original name to display) set _item(value: Item){ if (value) this.originalName = value.name; this.selectedItem = Object.assign({}, value); } }
In our template, we are toggling the title that we show the user based on whether or not we are dealing with an existing component or new component by using ngIf to detect if selectedItem.id exists. We then have two inputs bound to selectedItem.name and selectedItem.description using ngModel and the two-way binding syntax.
<div> <div> <h2 *ngIf="selectedItem.id">Editing {{originalName}}</h2> <h2 *ngIf="!selectedItem.id">Create New Item</h2> </div> <div> <form novalidate> <div> <label>Item Name</label> <input [(ngModel)]="selectedItem.name" placeholder="Enter a name" type="text"> </div> <div> <label>Item Description</label> <input [(ngModel)]="selectedItem.description" placeholder="Enter a description" type="text"> </div> </form> </div> </div>
And that is it! This has essentially been an exercise in taking data and passing it to the children components to display.
Events Up
The flip side to the “state down” maxim is that events always flow up. User interactions will trigger events that ultimately make its way to a reducer to be handled. The interesting thing about this approach is that your components suddenly become very lightweight and in most cases “dumb” in the sense that they perform zero logic. We could technically dispatch a reducer event within our children components but instead we are delegating that to the parent component to minimize dependencies in our components.
Let’s check out the ItemsList component without the template to see what I mean by this. We have a single input for our items array and then we have two event outputs we are emitting when an item is selected or deleted. That is the entire sum of the ItemsList class.
@Component({ selector: 'items-list', template: HTML_TEMPLATE }) class ItemList { @Input() items: Item[]; @Output() selected = new EventEmitter(); @Output() deleted = new EventEmitter(); }
In our template, we are calling selected.emit(item) when an item is clicked and calling deleted.emit(item) when the delete button is clicked. We are also calling $event.stopPropagation() when the delete button is clicked so that it doesn’t trigger the selected event handler.
<div *ngFor="#item of items" (click)="selected.emit(item)"> <div> <h2>{{item.name}}</h2> </div> <div> {{item.description}} </div> <div> <button (click)="deleted.emit(item); $event.stopPropagation();"> <i class="material-icons">close</i> </button> </div> </div>
By defining selected and deleted as component outputs, we can capture them in our parent component in the same way that we capture native DOM events like click. We see this in the code below as (selected)=”selectItem($event)” and (deleted)=”deleteItem($event)”. The $event parameter does not contain mouse information but rather the data that was sent with the event.
<div> <items-list [items]="items | async" (selected)="selectItem($event)" (deleted)="deleteItem($event)"> </items-list> </div>
When those events are emitted, we then capture them and handle them in our parent component. In the case of selecting an item, we are going to dispatch a new event with the action type of SELECT_ITEM and set the payload to the item selected. When an item is deleted, we will just delegate that to the ItemsService to be handled.
export class App { //... selectItem(item: Item) { this.store.dispatch({type: 'SELECT_ITEM', payload: item}); } deleteItem(item: Item) { this.itemsService.deleteItem(item); } }
For now, we are just going to dispatch a new event on store within our service to pass the DELETE_ITEM action to the reducer with our deleted item in tow. We will replace this with HTTP calls in a moment.
@Injectable() export class ItemsService { items: Observable<Array<Item>>; constructor(private store: Store<AppStore>) { this.items = store.select('items'); } //... deleteItem(item: Item) { this.store.dispatch({ type: 'DELETE_ITEM', payload: item }); } }
To reinforce what we have just learned, we will go through the ItemDetails component and follow the event flow up. We want to allow the user to save an item or cancel the operation and so we will define two outputs called saved and cancelled.
class ItemDetail { //... @Output() saved = new EventEmitter(); @Output() cancelled = new EventEmitter(); }
In the bottom of our form, we have a cancel button that calls cancelled.emit(selectedItem) on click and a save button that calls (click)=”saved.emit(selectedItem).
<div> <!-- ... ---> <div> <button type="button" (click)="cancelled.emit(selectedItem)">Cancel</button> <button type="submit" (click)="saved.emit(selectedItem)">Save</button> </div> </div>
In our main component, we then bind to the saved and cancelled outputs to call the event handlers in our class.
<div> <items-list [items]="items | async" (selected)="selectItem($event)" (deleted)="deleteItem($event)"> </items-list> </div> <div> <item-detail [item]="selectedItem | async" (saved)="saveItem($event)" (cancelled)="resetItem($event)"> </item-detail> </div>
When a user clicks the cancel button, we create an empty item and emit a SELECT_ITEM action. When an item is saved, we call saveItem on ItemsService and then reset the form..
export class App { //... resetItem() { let emptyItem: Item = {id: null, name: '', description: ''}; this.store.dispatch({type: 'SELECT_ITEM', payload: emptyItem}); } saveItem(item: Item) { this.itemsService.saveItem(item); this.resetItem(); } }
Originally, I wrestled with having a form for creating an item and then a separate form for editing an item. This seemed a bit heavy and so I opted to share the same form since both exist to save an item. I then approximate upsert functionality in the itemSave method by detecting the presence of item.id and calling either createItem or updateItem. Both methods take the item we send and dispatch it with the proper event. By now, I hope that the pattern of how we deliver objects to a reducer for processing is starting to emerge.
@Injectable() export class ItemsService { items: Observable<Array<Item>>; constructor(private store: Store<AppStore>) { this.items = store.select('items'); } //... saveItem(item: Item) { (item.id) ? this.updateItem(item) : this.createItem(item); } createItem(item: Item) { this.store.dispatch({ type: 'CREATE_ITEM', payload: this.addUUID(item) }); } updateItem(item: Item) { this.store.dispatch({ type: 'UPDATE_ITEM', payload: item }); } //... // NOTE: Utility functions to simulate server generated IDs private addUUID(item: Item): Item { return Object.assign({}, item, {id: this.generateUUID()}); // Avoiding state mutation FTW! } private generateUUID(): string { return ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11) .replace(/1|0/g, function() { return (0 | Math.random() * 16).toString(16); }); }; }
We have just completed the “state down, events up” circuit but our example still lives in a vacuum. How hard would it be to modify our application to communicate with a real server? The answer is “not hard at all!”.
Calling All Servers
First, a bit of setup to prepare our application to make HTTP calls. We will import Http and Headers from angular2/http.
import {Http, Headers} from 'angular2/http';
We will then define a BASE_URL constant so we only have to type it once and we will also create a HEADER constant to tell our server how we are communicating with it. This may not be necessary depending on what backend you are using but to get json-server working, I had to add it.
const BASE_URL = 'http://localhost:3000/items/'; const HEADER = { headers: new Headers({ 'Content-Type': 'application/json' }) };
We will modify our ItemsService constructor to include Http and assign it to a private local instance called http.
constructor(private http: Http, private store: Store<AppStore>) { this.items = store.select('items'); }
From here, let us modify our CRUD methods to handle remote server calls, starting with loadItems. We are calling this.http.get(BASE_URL) to get our remote items and because Http returns an observable, we can pipe our results through additional operators. We will call map to parse our results and then call map again to create the object we want to dispatch to our reducer. The combinations of map method calls is an observable sequence in that every result gets passed through those sequence of operations. To “tie off” a sequence, we will subscribe to it and then hand off control to our reducer by dispatching our transformed results.
loadItems() { // Retrieves the items collection, parses the JSON, creates an event with the JSON as a payload, // and dispatches that event this.http.get(BASE_URL) .map(res => res.json()) .map(payload => ({ type: 'ADD_ITEMS', payload })) .subscribe(action => this.store.dispatch(action)); }
We will follow a similar pattern when we update createItem. The only difference is that we are calling http.post with a formatted item as the payload and our HEADER constant. Once we have our results, we map everything to an object that we can dispatch in our subscribe method.
createItem(item: Item) { this.http.post(BASE_URL, JSON.stringify(item), HEADER) .map(res => res.json()) .map(payload => ({ type: 'CREATE_ITEM', payload })) .subscribe(action => this.store.dispatch(action)); }
Updating and deleting are a bit simpler in that we are not dependent on an object being returned from the server. We only care that the operation was successful. Because of that, we will use the http.put and http.delete and skip mapping the response entirely. We can dispatch an action to our reducer from the subscribe block as you can see below.
updateItem(item: Item) { this.http.put(`${BASE_URL}${item.id}`, JSON.stringify(item), HEADER) .subscribe(action => this.store.dispatch({ type: 'UPDATE_ITEM', payload: item })); } deleteItem(item: Item) { this.http.delete(`${BASE_URL}${item.id}`) .subscribe(action => this.store.dispatch({ type: 'DELETE_ITEM', payload: item })); }
BONUS: Testing
One of the most important aspects of redux is that it is very easy to test reducers because they are pure functions with a clear contract. In regards to our application, the surface area that contains testable logic has been vastly reduced. I wasn’t trying to be funny when I wrote that but in hindsight, it kind of is!
Setting Up
I am not going to get into the entire testing harness but let’s a take a quick tour of our test spec. The first thing we need to do is to import items and selectedItems as well as it, describe and expect from angular2/testing. Wait a second! Aren’t those Jasmine methods!? Yes they are and Angular 2 now uses Jasmine by default.
import {items, selectedItem} from './items'; import { it, describe, expect } from 'angular2/testing';
For reference, the skeleton of our spec looks like this.
describe('Items', () => { describe('selectedItem store', () => { it('returns null by default', () => {}); it('SELECT_ITEM returns the provided payload', () => {}); }); describe('items store', () => { let initialState = [ { id: 0, name: 'First Item' }, { id: 1, name: 'Second Item' } ]; it('returns an empty array by default', () => {}); it('ADD_ITEMS', () => {}); it('CREATE_ITEM', () => {}); it('UPDATE_ITEM', () => {}); it('DELETE_ITEM', () => {}); }); });
The tests were really easy to write because we start with an initial state and when we send an action to our reducer, we know exactly what we should get back. We know that if we dispatch an action of ADD_ITEMS, we will get back whatever we put in the payload, which we see in the assertion below.
it('ADD_ITEMS', () => { let payload = initialState, stateItems = items([], {type: 'ADD_ITEMS', payload: payload}); // Don't forget to include an initial state expect(stateItems).toEqual(payload); });
If we call the items reducer with an action type of CREATE_ITEM, we would expect that the result would be the initial array plus the new item.
it('CREATE_ITEM', () => { let payload = {id: 2, name: 'added item'}, result = [...initialState, payload], stateItems = items(initialState, {type: 'CREATE_ITEM', payload: payload}); expect(stateItems).toEqual(result); });
We can easily articulate the expected result for the remaining two reducer’s methods and then write assertions for them as I have below.
it('UPDATE_ITEM', () => { let payload = { id: 1, name: 'Updated Item' }, result = [ initialState[0], { id: 1, name: 'Updated Item' } ], stateItems = items(initialState, {type: 'UPDATE_ITEM', payload: payload}); expect(stateItems).toEqual(result); }); it('DELETE_ITEM', () => { let payload = { id: 0 }, result = [ initialState[1] ], stateItems = items(initialState, {type: 'DELETE_ITEM', payload: payload}); expect(stateItems).toEqual(result); });
Review
We have covered a lot of ground in this lesson and #higFive if you have made it this far! Let’s do a quick recap of what we did while it is fresh in our minds.
- The primary characteristics of redux is that state is centralized, events flow up and state flows down.
- The @ngrx/store implementation uses observables allows us to populate our templates using the async pipe.
- We created our reducers which are simple functions that take an action and state object and returns a new state object.
- Our reducer functions must be pure and so we saw how we could create them without mutating our collections
- A store is basically a key value value map with some mechanisms to handle events and emit state.
- We broadcast our events using store.emit.
- We subscribe to data using store.select.
- Create a local copy when working with a form to avoid higher level mutations.
- With asynchronous calls, we pass our results through an observable sequence and then emit the event to the reducer on completion.
- Reducers are really easy to test because the methods are pure and the contract is crystal clear.
Learning redux via @ngrx/store has been the closest thing to that “new programmer” feeling that I have felt in awhile. It is been so much fun! Take the example and play around with it and think about how you can use this approach in your day to day projects. If you create something awesome then by all means, share it in the comments for everyone to check out!
Huge Shout Outs
First and foremost, a big #highFive to Dan Abramov for creating such an awesome library. I owe Rob Wormald a huge debt of gratitude for creating @ngrx/store and being a super awesome and patient friend while I wrapped my mind around this brave new world. I never ever take it for granted the amazing friends that I have who take time to help me write the best possible blog posts that I can. Thank you, Joe Eames for your very thorough and thoughtful input as well as Victor Savkin and Tero Parviainen for their great feedback and encouragement. Also, a very warm Angular [(hug)] to Gleb Bahmutov for his special Russian brand of tough love.
Resources
Egghead.io – Getting Started with Redux
Code
Hi,
very nice article. There is one thing I’d recommend though: maybe have just one subscription in the ItemsService? Take a look at https://github.com/ngrx/angular2-store-example/blob/master/src/app/users/models/users.ts to see what I mean.
Cheers.
Awesome!
First of all – wanted to say what a great post it is! I really enjoyed reading it.
I have a question, regarding this piece of code:
export class App {
items: Observable<Array>;
selectedItem: Observable;
constructor(private itemsService: ItemsService, private store: Store) {
this.items = itemsService.items; // Bind to the “items” observable on the “ItemsService”
this.selectedItem = store.select(‘selectedItem’); // Bind the “selectedItem” observable from the store
}
}
I know that it is considered a bad practice to hold a reference to external items (service properties, for example), inside your component. Is it different when dealing with observables?
Thanks!
Yaniv
Most enjoyable and informative article on the new generation of Web apps, never cease to be amazed at how many devs can collide to make a great app with new methods.
Great article. Did you come across immutable.js? What do you think about it?
Thanks! I have been playing around with immutable.js and ramda.js and I think they are both pretty rad. I would encourage developers to understand how to do immutable operations by hand initially to help lock down the concepts. For me, functional programming is not a binary “I didn’t understand it. Now I understand it!” but rather something that I work on and refine as I apply it in practice. Dan Abramov’s videos on immutables operations was a game changer for me.
Lukas,
Thank you for the article!
Please, consider to fix the following issue:
The item-detail component doesn’t update when deleting the selected item. It still has data of the deleted item. After pressing the Save button, you get the 404 (Not Found) error.
Lukas,
As I understand, the reducers ‘items’ and ‘selectedItem’ work independenly.
But actions ‘UPDATE_ITEM’, ‘DELETE_ITEM’ should also affect the field ‘selectedItem’ in application state.
When I run your code I get error TS2300: Duplicate identifier ‘_item’ referring to
class ItemDetail {
@Input(‘item’) _item: Item;
originalName: string;
selectedItem: Item;
@Output() saved = new EventEmitter();
@Output() cancelled = new EventEmitter();
set _item(value: Item){
if (value) this.originalName = value.name;
this.selectedItem = Object.assign({}, value);
}
}
Yeah, I get that as well. Everything still runs so ¯_(?)_/¯
There is an alternate implementation that Victor Savkin told me about that I will try and see if that fixes the error. Good eye though!
Hi Lukas,
nice done. Especially you ItemService-implementation was the pattern I looked for to sync our frontend with the backend.
Two thoughts about your-redux pattern:
1) Your state-down explains it very fine. But usually the TopComponent and
consumer of the Store is not the AppComponent, when you use a router. Its then always the component, which is referenced by the selected route.
2) We use currently a bit less centralized usage of the store.select, and allow dummy-subcomponents also to subscribe to the store directly (Usually the static parts, like source-arrays for dropdowns) . The background was that our subcombonent-tree was increasing and relating to that the Input/Output signatures of the topComponents increased too, because more and more data was necessary in sum. As long as the state-down, event-up flows are intact, it’s not breaking the central pattern.
Da5id, I got the same error. I fixed it by removing the _item property and putting the @Input decorator on the setter, like so:
@Input(‘item’) set item(value: Item) {
…
}
Lukas, thanks for a very clear guide to ngrx!
This article is so amazing! Apart from those retarded graphs/pics. Where did you get those from?
Cheers!
If you run into an error related to “node_modules/angular2/src/facade/promise.d.ts” just add the following to the top of the file:
declare var Promise: PromiseConstructor;
My 4 year old made them and now he is crying. 😉
Nice article. It compliments Dan’s very well. But I have a question about how to retrieve states that are deeper in the hierarchy if I am only providing the app reducer to the provideStore(app) method in the providers configuration for my app @Component. You see, I wanted to create composite reducers like how Dan described. So my app reducer calls user(), _public() and shell(). the public reducer calls the login reducer, and so on, down the store hierarchy. So if my app component only provides the app reducer, how can my login component retrieve the it’s state via Store.select() when it actually resides in app.public.login in the store hierarchy? Did I word this understandably?
Thanks & kind regards,
Robert
Thanks, Robert! If I understand your question correctly, you can abstract the state provided by your store into a service and then make that available wherever you want. I would recommend having a clearly defined composition of smart (layout) components and dumb components that simply render state handed to it via @Input and delegate event handling via @Output. Pull your state into your smart component and then just pass it to the relevant child components as needed.
I have done this in the ItemsService but you could extend this example much further and really do some interesting things with RxJS and Observables.
The graphics are the best part!!!! Dont let anyone tell you different.
Hi Lucas – great post! I think it’s a really nice road to go down.
I have a comment/question on your async operations implementation. Can you elaborate a bit on why you choose a different path for that, than Redux ? In the Redux world, for async scenarios, you still just dispatch actions (although these actions are really action-funcs (‘thunks) that are processed by redux-thunk middleware). By doing it that way, the angular components doesn’t have to reason about whether they are triggering async operations or not, so sync / async looks exactly the same and you hide the complexity in an action-creator (which could use the ItemsService for the details). IMHO I think that’s a better solution. Here’s a link to the “the react way”: http://redux.js.org/docs/advanced/AsyncActions.html.
Good question Carsten 😀 Other than the most simplistic scenarios, all interactions with the store would be abstracted into a store so that your components are oblivious to where the logic is happening or the data is coming from. This solves the bit about whether or not your components need to reason about the nature of the action.
Once inside the service, I think it comes down to preference. Coming from an Angular context, I haven’t found a need (yet) to create that additional abstraction. Scott Moss is a huge fan of Redux and loves thunks and so his approach is a bit different than mine. I personally like the symmetry of having a service in front of the reducer for async operations and a service after the reducer for computed observables. A great dinner conversation and I may change my mind entirely 😀
Could you please unhide your article’s date of publication?
With the weekly changing ecosystem of utilizing angular2/react + flux flavor + obvervables + (insert new idea library) it’s important to be able to compare the codebases from multiple articles mixing and matching these ideas.
AsyncActions are nice pattern. However, in case of Angular (1 and 2) there is a one caveat: how can I use DI with that ? For example, how to use $http service in the action ? Of course, you can use some middleware, for example redux-promise and just send promise as action param (but we want rxjs!). You can pack your actions in injectable service, but it also seems too complicated. I like Lukas approach as with services we can support really complicated scenarios.
The sample repository with the package versions is going to be the best indicator of how current the concepts are in. For the most part, very little has changed outside of a few interface additions since I wrote this and the concepts are still on point.
You made me feel that i am home again
Hi, thanks for the great tutorial!
Just wondering if you have any insights on how best to implement server error responses into this workflow, ie 4xx, 5xx, http errors?
Cheers
First off, this is a totally amazing and helpful article. I can’t thank you enough!
I tried to start the code from github and get the following your instructions exactly
[1] ERROR in ./client/vendor.ts
[1] Module build failed: A file specified in tsconfig.json could not be found: C
:\Users\anderjh\ngrx-rest-app\typings\index.d.ts
[1] @ multi vendor
[1]
[1] ERROR in ./client/bootstrap.ts
[1] Module build failed: TypeError: Cannot read property ‘getEmitOutput’ of null
[1] at Object.loader (C:\Users\anderjh\ngrx-rest-app\node_modules\ts-loader\
index.js:432:33)
Love the write up! I’ve been running through as many examples as I can find to try and wrap my head around the proper project setup for this with more complex data models. I’ve been looking at the example app they have linked off the ngrx store github readme.
https://github.com/ngrx/example-app
Just curious if you’ve played at all with the effects ngrx library [ https://github.com/ngrx/effects ]. They make use of it and several other ngrx projects in this example. I’m trying to understand if while I’m learning redux and ngrx if I should even be looking at this library yet.
I’d love to hear your thoughts and thanks again on a great write up.
This is interesting. I was wondering about how the service and reducer are both used. I need a service to maintain subscriptions and the like, and am trying to figure out how to structure the data flows. Here getting the list is through the service, setting the data is through the service, but getting individual items is through the reducer. That seems to step on the basic rules of redux, where everything goes through the reducer functions.
In my app I will have multiple services which talk to a local and non local data source with subscriptions and the like, one each per collection. Ngrx is nice for simplifying the data in and out, setting observables etc, but I need the background service.
Now to explore the @Effects pattern where a dispatch to the reducer function can tie in to the services. It seems the Effects class in the example takes an instance of the service, a database instance and the service where the type: payload: stuff is named and encapsulated, allowing a response to the various dispatch calls to do database stuff or other updates needed. This seems to fit better in the redux rules where all the data changes are dispatched to a reducer.
I really liked this article… with one singular exception:
Your application component manages the store and the itemService (persistence delegate) separately. This is not only error prone but this exposes needlessly two different services. I think the Store should be a facade to both the immutable data (with the reducers) AND the persistence services. Then only the Store would be injected into components.
Thomas,
But doing this would force the injection of the http operations into the reduced function, which would turn it into an “impure” function. And, according to Dan Abramov, a redux function must be “pure”.
Have you thought how to approach api service calls error handling?
Specifically, lets say that createItem(item: Item) fails some server side validation returning bad request status code and needs to be displayed to user in ‘item-detail’ component.
@Daniel – Sure… I would handle that in the service layer that sits in front of the ngrx store. This gives you the window to decide what you want to do before you send it to the reducer. If you need to display errors, you can create a store specifically for that and dispatch an error action with the error details as the payload.
Nice article. The only question that I been pondering over redux/@ngrx-store/effect concept is: Does one store all of the state and async state (API calls) in a store? Even if one navigates away from a view that’s not using that state? concern of size of the state ?
Thank you for the article..
Hi Lukas,
Great article I am trying to use it in my current project. I have one question. How do you handle cache?
I was trying to use this approach https://github.com/thelgevold/angular-2-samples/tree/master/components/rxjs-caching.
However, I could not make it work. Do you have any suggestions?
Thanks for this article and your course on Fron End Masters https://frontendmasters.com/courses/reactive-angular-2/
@Jeff Anderson do a >typings install
should fix it…
Great article !, thanks….
Any idea how to avoid the Duplicate identifier ‘_item’ issue ?
Can’t build prod version…
Thanks
Suppose there is another smart component that needs items data from store. Would then both components at their init/ctor call loadItems method? (so which ever component gets navigated to by user first makes the initial load)
@Ohad, answer is in previous comments 🙂
Hello, nice article but … what about the errors handling and loaders ?
I handle all the async bits in a service that sits in front of the reducers. If something goes wrong then you handle it before you dispatch your action. Basically, the same concept as a thunk but we are rolling our own.
Thanks for writing this – most examples don’t include communicating with a real server, so this was nice to read.
Just a minor nitpick – shouldn’t “When an event is dispatched from the store with an action type of SELECT_ITEM, it will hit …” say “When an event is dispatched to the store with an action type of SELECT_ITEM, it will …” ?
That’s how I interpreted it anyways – just wanted to make sure my understanding is correct. Thanks again!
Also just wanted to clarify this – the AppStore interface is just for us to visualize / understand our app’s overall store structure, right? We could remove the AppStore interface (and change all the Store’s to Store>) because it isn’t actually being used – it’s just for the developer to see the app’s model structure, right? When we do “this.store.select(‘items’)”, does it actually check that the “items” property exists in the AppStore interface?
Hey,
first of all congratz for this really nice and helpful article. It’s a good entry point for both, redux and angular2 in my opinion. However I’m wondering how to use this kind of “architecture” in some more complex circumstances.
In my current application I’m having two services used within a component, where data from the first service is gathered, manipulated by user interaction and afterwards this data is needed as input data for the second service. But there i’m running into infinite loops as the component has to subscribe the data as it is held in the store. How can I avoid such a looping when using the described architecture?
I have one question that confused me for a long time
I know how Redux work
But when i look at my APP UI,
it is very hard for me to identify the state
because it is very ambiguous.
i dont know which should put in the state,
for example, the tutorial u provided,
How can u know state of the app is “items” & “selectedItem” ?
i really have no idea …
How can i find out the exact state which can be used with Redux ?
could someone please tell me the process ?
or such process just come from the experiences ?
is there a best practice for identifying the state of the APP ?
please help, thanks
Great article, but i have one simple question, how and where to create files ?? the code written above, starting with the 2 reducers,
where should these files be created..??
I love the idea of a central store, etc, but I’m a little concerned about the tight coupling taking place here. I can see the logic of some ‘parent’ component being responsible for setting input properties on child components but for output events bubbling up the tree in all trivial applications, well, it’s a bad idea. I happen to have a ‘logical parent’ component, a term I apply to a grouping of components governed by one component in the manner described here. Setting input properties down through the hierarchy is fine, but a choice made at a leaf component say 3 levels down needs to be handled by a different child of the logical parent. Bubbling up events to the parent only for it to hit the store, subscribe to the portion of the store and then set an input on the child that needs it is crazy. That’s the reason loosely coupled event brokers were created in the first place! Then you have to ask where you stop? There’s another component hanging dirclty off AppComponent that needs the same information. You’d have to change the logical parent to be AppComponent and introduce input and output parameters down through the hierarchy.
Andrew – you make some good points. To be clear, I am not a fan of forcing an application into blunt shapes so that it fits into a specific pattern. I believe that a non-trivial application will have layers of container / presentational components where they communicate directly with a service where it makes sense. The nature of how the application interacts with the data model should provide some insight where these lines of communication should be. For instance, if a component is three layers deep but by nature, handles state that unique at that level, I would consider bringing in a service to simplify the communication channel. Personally, I think of container / presentational components as being only two or three levels deep and try to compose my applications as such.
The challenge with tutorials is that we try to communicate complex concepts in simple terms that people can easily grasp which means we have to bypass some of the nuances that exist in real life.
Lukas
When you think about this, had Angular 2 been designed from the ground up with the concept of Redux, would it even have had Input or Output decorators? The purpose of Inputs is to provide a component with some data it needs to fulfil its role, whilst an Output is to convey information to the outside world. That is precisely what Redux is all about, except in a loosely coupled way. Inputs would be ‘store.select’, whilst Outputs would be ‘store.dispatch’. I’d go so far as to say that perhaps the logic for subscribing and dispatching to the store should live at the most granular level possible, hence a self-contained component that has no coupling to any other component. I could of course be wrong, but I think it’s an interesting discussion. I’d rather be consistent with my component communication throughout my application than have a mix and match approach that is difficult to maintain.
On the surface, this seems correct until a user action requires additional business logic such as an asynchronous action to be performed (saving data to the server) before it goes into the application store. You want your components to be as thin as possible and do two things; consume enough data to satisfy the view and communicating user actions to a service for handling. There are some user actions (toggle visibility, etc) that do not affect the application model and those can be handled at the component level but as a whole, the less your component does the better. If a component is completely stateless via inputs and outputs, it is completely stable unless something upstream has broken.
Redux as a pattern is incredibly simple and non-prescriptive in a lot of ways. To that, I recommend using as much of it or as little of it as you want. For me, I prefer to have a service before the store to handle business logic and if I need to do computed models, I have a service on the backside to handle that for me. Also, you are always going to have components coupled to each other because that is the nature of the component tree. Inputs and outputs are just a really great way for declaring a contract with the outside world. It is the API of the component.
Lukas
This is a very interesting discussion. I have to agree with Andrew on this point. With regards to communicating with a service everything to me should just be communicated using Actions. An async call to a service is clearly an effect in this respect and this very well catered for using ngrx/effects. I have been using ngrx/store and effects wth great success. Just dispatch an action and it will either be handled by a reducer or some data update or by an effect in the case of any side effects like hitting an http service. It makes your application much easier to reason with. I find Outputs, and to some degree Inputs, can lead to overly complex code. More on ngrx store and effects here:
https://github.com/ngrx/store
https://github.com/ngrx/effects
Also, I’m not convinced by the Smart/Dumb component pattern. What happened to single responsibility. Pushing responsibility up to a god parent isn’t the way to go about it imo. The only case for that and Inputs/Outputs I think is if you are developing a reusable component which depends on the context of it’s usage. In the real world though most of our components have a specific responsibility. The responsibility being to subscribe to slices of the store and dispatch actions for the given functionality.
I am loving this discussion! I think both of you make excellent points that I need to consider some more. What I can say is that this approach requires total buy-in to redux as you are forgoing coupling a component to another component in exchange for coupling a component directly to ngrx (in this case). I know from first-hand experience that this is a luxury as most of my enterprise clients are really keen on creating reusable components that can be shared across multiple projects. And believe it or not, not everyone “gets it” when it comes to redux. I often times have to pitch as something that sits behind a service layer or I would never get buy in. It is just too radical.
In conclusion, I love this alternate approach and I will look into it. In reality, there are other factors such a pre-existing architecture, design goals, comfort levels that may require a hybrid redux / component driven approach.
I come from 16 years of .NET experience mostly as a contractor in the Investment Banking/Financial sector and I know first hand the issues of tightly coupled events/components. UI frameworks such as PRISM (for WPF) included loosely-coupled event aggregation and it was a joy to use compared to the alternative. All of a sudden I could subscribe to events taking place in other modules without concern for who was raising them and I didn’t have to worry about raising events up my component hierarchy, I just published them to the EventAggregator. Simple Rx implementations of this ‘messenger’ replaced the PRISM-bundled EventAggregator, which made it easier to subscribe to the events of interest. I see Redux and in particular ngrx/store as a very similar thing. It’s easy to see the perceived advantages of input and output parameters in simple scenarios. It requires less thought if you bypass the store but past experience has shown that things may start out simple but that can quickly change and you’re left with a major refactoring job to ‘do it right’. I’m also a fan of adopting a constant approach to issues like this, though I share the view that a simple event that doesn’t change the model shouldn’t go via the store.
Nice!
Wondering – have you ever tried doing the HTTP calls to the server in a resolver? So you would dispatch the action which would resolve the new state then run the HTTP call.
I’m interested in this different approach because it would enable offline/online syncing. So you check during the resolve if the user has connectivity. If no, then mark that action with property offline: true. When the user gains connectivity, go through the history of actions for those marked offline: true and then perform their async calls as well as returning a new state with the new uuids.
Thoughts?
Nice Article !!
Great article.
I’m new tthe redux way of thinking, and I have a doubt.
When user one opens the app, a store will be created for him. So, any changes in it will reflect in the views/template. This is great.
But if there is another user accessing, the he will have his own store. Changes mades from user one won’t reflect in the second user’s store, only if the user does anything that will refresh (and request) new data, as far as I understand the flow.
Is there any good way to do the API to be reactive too? Is there any good stack for this (I mean, for example, use Firebase and its events, Mongo DB, or something with Rails 5)?
[]S
Hi,
I made a big part of the tutorial in my github (not the testing part)
I made some little arrangements in the code cause of the release of angular
Have a look and tell me what you think
https://github.com/raiden0610/ngrx-store-poc
Cheers
Great article. It was very helpful. Thank you.
Hi there! Couldn’t agree more with your ah ha! moment 🙂 Great visuals also, very useful! Recently a teammate published an article with some real examples of how Redux can be used and the value of this savvy technology in a simple and practical way. Maybe it can be of your interest! Take a look: https://blog.uruit.com/2017/05/04/using-redux-angular/
I tried creating the app using angular-cli. The link to the app is ‘https://github.com/VaibhavP17/ng2-rx-app’. But I have a bug in it, that whenever I click on the items list, the selectedItem is populated as it should but the items list vanishes. What could be wrong with that? I have been scratching my head for a very long time now 🙁 Would be great if anyone could help me with that.
I have even reported it as a bug in the repo. (https://github.com/VaibhavP17/ng2-rx-app/issues/4).
Thanks in advance.
You are not returning default state in the items.store.ts if no action type matches
Hi Lukas,
This is nice article.
How do you think should we inject store to all smart component or we need to abstract it out as some Model or Service.
Every smart component can deal with either store and they know about dispatching some action or we can create Model with some behavior and which takes store and dispatches action. Hence we will create an Model facade over UI components. Similarly we could have some Query models too.
Hi, Rupesh – great question 😀 There are some nuances to this question and so this is not a one size fits all answer but I would recommend using @ngrx/effects and reselect to sit on either side of the store to handle async operations and relational, computed data. What this accomplishes is that I ONLY have to consume data from the store and dispatch actions back up to it. Minimizing the surface area of your components to the rest of the application is a good thing.
“The flip side to the “state down” maxim is that events always flow up”. Unless you’re modifying local state using ngModel, which originally wasn’t even going to be included in Angular 2 until the community complained. I think it’s convenient, but it might be easier in the long run to just dispatch actions for everything and keep it consistent. You could have a personEdit reducer or something.
Hi,
Thank you for this great article again. !
Do you think is a good think to keep fill up the states ( stores ) , I mean if you can for example select MULTIPLE items instead of one item , and each time you select item then the store of selected items will be filled with multiple items ( say 1000 items ) , and after moving to the next page ( we can get the selected items of course ) but then finish from that component / page , we should free it up ( the selecteditems store ) right ? the memory will be filled up .
Next think if I have a two model visit and client and I need to select a client then make a visit on him/her will make me make a new state/reducer/action for selected_client so when I start the visit I can have the selected client , can I make action VISIT_START and the payload be the user object ( so I have action with different type of the payload ) ?
Thanks,
Thanks for this great article! If I understand it correctly, redux is all about formalizing state handling – there is one central place for state (store) and there are only well defined ways to change that state (reducers).
However, in your example, the ItemDetail component manages some state locally (original display name, copy of item). What qualifies this state to be held locally within the component rather than being stored centrally? Why is the component responsible for creating the copy and keeping the original display name rather than the SELECT_ITEM reducer?
Hi, Oliver – the reason I am creating a copy within the detail component is so that I can isolate the state mutation until it is ready to be committed back to the application state. This allows for a user to easily cancel out of an update because it hasn’t been propagated to the rest of the application. I also am storing the original title for UX reasons so that the user can keep mental track of the original item they editing. This will vary from situation to situation but it is important to make the distinction between view state and application state. View state should be managed by the component and deals specifically with how the user is interacting with the component at any given time while application state should be handled by the application store and is available to any part of the application for consumption.
Hi, Lukas
When I run your code I get three errors such as :
” Type ‘Observable<{}>’ is not assignable to type ‘Observable<Item[]>’.
Type ‘{}’ is not assignable to type ‘Item[]’.
Property ‘length’ is missing in type ‘{}’. ”
Both in items.service.ts, items.component.ts and widgets.components.ts
How can I solve these errors ?
Thanks,
Cyril
Hey Cyril,
We just pushed an update to the repo that fixes these type errors. Pull the latest from the master branch and you should be good to go.
Thanks!
Awesome post, I am new to angular-ngrx. This is the first blog post I have found that explains ngrx to a point where I understand.