Gerard Sans | @gerardsans
Spoken at 65 events in 23 countries
ngrx
Redux
Dan Abramov
source: blog
1
2
3
4
5
Rob Wormald
source: blog
1
2
3
4
5
A
A
> ng new ngrx-entity-todo
> npm install @ngrx/schematics --save-dev
> npm install @ngrx/{store, effects, entity, store-devtools} --save
> ng generate store State --root --module app.module.ts --collection @ngrx/schematics
> ng generate effect App --root --module app.module.ts --collection @ngrx/schematics
> ng generate entity Todo -m app.module.ts --collection @ngrx/schematics
// src/app/reducers/currentFilter/currentFilter.actions.ts
import { Action } from '@ngrx/store';
export enum CurrentFilterActionTypes {
SetCurrentFilter = '[Filter] Set current filter'
}
export class SetCurrentFilter implements Action {
readonly type = CurrentFilterActionTypes.SetCurrentFilter;
constructor(public payload: { filter: string }) {}
}
export type CurrentFilterActions = SetCurrentFilter;
// src/app/reducers/currentFilter/currentFilter.reducer.ts
export const initialState: string = 'SHOW_ALL';
export function reducer(
state = initialState,
action: CurrentFilterActions): string
{
switch (action.type) {
case CurrentFilterActionTypes.SetCurrentFilter:
return action.payload.filter
default:
return state;
}
}
// src/app/reducers/todo/todo.reducer.ts
export enum TodoActionTypes {
AddTodo = '[Todo] Add Todo', UpdateTodo = '[Todo] Update Todo', ...
}
let currentId = 1;
export class AddTodo implements Action {
readonly type = TodoActionTypes.AddTodo;
constructor(public payload: { todo: Todo }) {
payload.todo.id = currentId++;
}
}
export type TodoActions = LoadTodos | AddTodo | UpsertTodo | AddTodos
| UpsertTodos | UpdateTodo | UpdateTodos | DeleteTodo | DeleteTodos | ClearTodos;
// src/app/reducers/todo/todo.model.ts
import { EntityState } from '@ngrx/entity';
export interface Todo {
id: number;
completed: boolean;
text: string;
}
export interface Todos {
todos: EntityState<Todo>;
}
import * as todos from './todo/todo.reducer';
import * as currentFilter from './currentFilter/currentFilter.reducer';
export interface Filter {
currentFilter: string;
}
export interface Todos {
todos: EntityState<Todo>;
}
export interface TodosState extends Todos, Filter { }
export const reducers: ActionReducerMap<TodosState> = {
todos: todos.reducer,
currentFilter: currentFilter.reducer
};
// ngrx v4
// export const getTodos = state$ => state$.select(s => s.todos);
// export const getCurrentFilter = state$ => state$.select('currentFilter');
export const getTodos = state$ => state$.pipe(
select('todos'),
map(todoEntity.selectAll)
);
export const getCurrentFilter = state$ => state$.pipe(
select('currentFilter')
);
import { StoreModule } from "@ngrx/store";
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { AppComponent } from './app.component';
import { reducers, metaReducers } from './reducers';
@NgModule({
imports: [
BrowserModule,
StoreModule.forRoot(reducers, { metaReducers }),
!environment.production ? StoreDevtoolsModule.instrument() : [],
],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
1
2
3
4
5
@Component({
template: `
<todo *ngFor="let todo of todos | async | visibleTodos:currentFilter">
{{todo.text}}
</todo>`
})
export class App implements OnDestroy {
constructor(private _store: Store<TodosState>) { }
public ngOnInit() {
this.todos = this._store.let(getTodos);
this._store.let(getCurrentFilter).subscribe((filter: string) => {
this.currentFilter = filter;
})
}
}
@Component({
template: `<input #todo (keydown.enter)="addTodo(todo1)">`
})
export class AppComponent {
private addTodo(input) {
if (input.value.length === 0) return
const todo: Todo = {
id: null,
text: input.value,
completed: false,
}
this._store.dispatch(new todoActions.AddTodo({ todo }))
input.value = ''
}
}
// add new todo
{
type: '[Todo] Add Todo',
payload: {
todo: {
id: 1,
text: 'Learn Dutch',
completed: false
}
}
}
export const adapter: EntityAdapter<Todo> = createEntityAdapter<Todo>();
export let initialState: State = adapter.getInitialState();
export function reducer(state = initialState, action: TodoActions): State {
switch (action.type) {
case TodoActionTypes.AddTodo: {
return adapter.addOne(action.payload.todo, state);
}
...
default: { return state; }
}
}
export const {
selectIds, selectEntities, selectAll, selectTotal
} = adapter.getSelectors();
{
todos: {
ids: [ 1 ],
entities: {
'1': {
id: 1,
text: 'Learn Dutch',
completed: false
}
}
},
currentFilter: 'SHOW_ALL'
}
@MikeRyanDev
@robwormald
@brandontroberts
@toddmotto
gsans/ngrx-entity-todo-app