I've been trying @ngrx/entity in a dummy "Todo" project, with a single AppModule, a single reducer and a single component. However, I am having issues trying it out.
My actions are pretty straight forward, just some CRUD operations :
import { Action } from '@ngrx/store';
import { Todo } from '../../models/todo';
export const CREATE = '[Todo] Create'
export const UPDATE = '[Todo] Update'
export const DELETE = '[Todo] Delete'
export class Create implements Action {
readonly type = CREATE;
constructor(public todo: Todo) { }
}
export class Update implements Action {
readonly type = UPDATE;
constructor(
public id: string,
public changes: Partial<Todo>,
) { }
}
export class Delete implements Action {
readonly type = DELETE;
constructor(public id: string) { }
}
export type TodoActions
= Create
| Update
| Delete;
Then my reducer file contains everything I need to handle my entity :
import * as actions from './todo.actions';
import { EntityState, createEntityAdapter } from '@ngrx/entity';
import { createFeatureSelector } from '@ngrx/store';
import { Todo } from '../../models/todo';
export interface TodosState extends EntityState<Todo> {}
export const todoAdapter = createEntityAdapter<Todo>();
export const initialState: TodosState = todoAdapter.getInitialState();
export function todoReducer(state: TodosState = initialState, action: actions.TodoActions) {
console.log("Got new action", action);
switch(action.type) {
case actions.CREATE:
return todoAdapter.addOne(action.todo, state);
case actions.UPDATE:
return todoAdapter.updateOne({
id: action.id,
changes: action.changes
}, state);
case actions.DELETE:
return todoAdapter.removeOne(action.id, state);
default:
return state;
}
}
export const {
selectIds,
selectEntities,
selectAll,
selectTotal
} = todoAdapter.getSelectors();
In my app.module.ts
file, I am doing the following :
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { AppComponent } from './app.component';
import { todoReducer } from './reducers/todo.reducer';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
StoreModule.forRoot({
todo: todoReducer
}),
StoreDevtoolsModule.instrument({maxAge: 25}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Finally, in my app.component.ts
, I am simply trying to create two TODOs :
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import * as fromTodo from './reducers/todo.reducer';
import { Todo } from '../models/todo';
import { Create } from './reducers/todo.actions';
@Component({
selector: 'app-root',
template: `
`,
styles: []
})
export class AppComponent implements OnInit {
public todos: Observable<Todo[]>;
constructor(private store: Store<fromTodo.TodosState>) {
this.store.dispatch(new Create({
title: "Test todo",
content: "This is a test todo",
date: new Date()
}))
this.store.dispatch(new Create({
title: "Test todo 2",
content: "This is another todo",
date: new Date()
}))
}
ngOnInit() {
this.todos = this.store.select(fromTodo.selectAll);
}
}
However, after running this, I inspected the Redux DevTools
. I saw that it only creates the first TODO, and its id is "undefined".
My console.log
in my reducer displays @ngrx/store/init
, as well as both [TODO]
Create actions
Moreover, if I try to ngFor | async
through my todos
in my component, I get various errors depending on what I try ("Cannot read 'map' property of undefined" mainly).
After some research, I noticed that @ngrx/entity
uses the id
property of the model you use.
In my case, my Todo
model did not have any id
property, so @ngrx/entity
could not handle my entities.
I thought it generated ids internally, but apparently it doesn't.
So the fix to this issue is to add an id
property to the model, and auto-generate it each time you add an item to the state.
There is a Angular2 UUID module for example.
In my case, I am using ngrx
with AngularFire2
, which has a createId()
method : const id = this.afs.createId()
. Then I can add it to the item I want to add, and then store it in my Firestore database.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With