Reputation: 2800
I'd like to implement Redux in a simple Angular app. As a tryout, I'd like to make a simple todo list, but I struggle to make it work.
Here is a sample code:
app-module.ts
import { NgRedux, NgReduxModule } from '@angular-redux/store';
import { IAppState, rootReducer, INITIAL_STATE } from './store';
import { TodoListComponent } from './todo-list/todo-list.component';
@NgModule({
declarations: [
AppComponent,
TodoListComponent
],
imports: [
...
NgReduxModule
],
...
})
export class AppModule {
constructor(ngRedux: NgRedux<IAppState>) {
ngRedux.configureStore(rootReducer, INITIAL_STATE);
}
}
store.ts
import { ADD_TODO } from './actions';
import { tassign } from 'tassign';
export interface IAppState {
lastUpdate: Date;
todos: Todo[];
// I tried `any[]`, but it didn't help.
}
export const INITIAL_STATE: IAppState = {
lastUpdate: null,
todos: []
//tried todos: new Array() too, but didn't make a difference };
export function rootReducer(state: IAppState, action): IAppState {
switch (action.type) {
case ADD_TODO:
return tassign(state, {
todos: state.todos.concat(action.todo),
lastUpdate: new Date()
});
}
return state;
}
todo.ts
export class Todo {
title: string;
}
todo-list-component.html
<h1>Todo List</h1>
<input type="text" #title>
<button (click)="addTodo(title)">Add</button>
<ul>
<li *ngFor="let todo of todos | async">
<span>{{ todo.title }}</span>
</li>
</ul>
todo-list-component.ts
import { Component } from '@angular/core';
import { NgRedux, select } from '@angular-redux/store';
import { IAppState } from '../store';
import { ADD_TODO } from '../actions';
import { Todo } from '../todo';
export class TodoListComponent {
@select((s: IAppState) => s.todos) todos: Todo[];
constructor(private ngRedux: NgRedux<IAppState>) {
ngRedux.subscribe(() => console.log(ngRedux.getState()));
}
addTodo(input) {
if (!input.value) {
return;
}
const todo = new Todo();
todo.title = input.value;
this.ngRedux.dispatch({
type: ADD_TODO,
todo: todo
});
input.value = '';
}
}
and package.json
{
"name": "angular-demo",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular-redux/store": "^7.1.1",
"@angular/animations": "^5.2.7",
"@angular/cdk": "^5.2.3",
"@angular/common": "^5.2.4",
"@angular/compiler": "^5.2.4",
"@angular/core": "^5.2.4",
"@angular/forms": "^5.2.4",
"@angular/http": "^5.2.4",
"@angular/material": "^5.2.3",
"@angular/platform-browser": "^5.2.4",
"@angular/platform-browser-dynamic": "^5.2.4",
"@angular/router": "^5.2.4",
"core-js": "^2.4.1",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"immutable": "^3.8.2",
"redux": "^3.7.2",
"rxjs": "^5.1.0",
"tassign": "^1.0.0",
"typescript": "^2.7.2",
"web-animations-js": "^2.3.1",
"zone.js": "^0.8.4"
},
"devDependencies": {
"@angular/cli": "1.7.3",
"@angular/compiler-cli": "^5.2.4",
"@angular/language-service": "^5.2.4",
"@types/jasmine": "2.8.6",
"@types/node": "~9.4.6",
"codelyzer": "~4.2.1",
"jasmine-core": "~3.1.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-cli": "~1.0.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"karma-coverage-istanbul-reporter": "^1.2.1",
"protractor": "~5.3.0",
"ts-node": "~5.0.0",
"tslint": "~5.9.1",
"typescript": "~2.7.2"
}
}
So when I type some text and hit Add
, I'd expect it to be added to the todos
list and displayed underneath the textbox.
But instead, I got a js error on the console, saying:
ERROR TypeError: Cannot read property 'todos' of undefined at TodoListComponent.addTodo (todo-list.component.ts:25) at Object.eval [as handleEvent] (TodoListComponent.html:4) at handleEvent (core.js:13547) at callWithDebugContext (core.js:15056) at Object.debugHandleEvent [as handleEvent] (core.js:14643) at dispatchEvent (core.js:9962) at eval (core.js:10587) at HTMLButtonElement.eval (platform-browser.js:2628) at ZoneDelegate.invokeTask (zone.js:421) at Object.onInvokeTask (core.js:4740)
If I check it in debugger, this.ngRedux.getState()
is undefined
, therefore it doesn't find the todos
property. So it seems that the initialization goes wrong, but I don't see why.
Any ideas what I'm doing wrong?
Upvotes: 1
Views: 2410
Reputation: 9815
You are missing the default return in your reducer function
export function rootReducer(state: IAppState = INITIAL_STATE, action): IAppState {
switch (action.type) {
case ADD_TODO:
return tassign(state, {
todos: state.todos.concat(action.todo),
lastUpdate: new Date()
});
default: return state;
}
}
Because initially there is no action dispatched and therefore the initial-state must be returned.
Upvotes: 2