Reputation:
I am learning angular 2 and following along with this tutorial:
To me this is all because an instance of my Todo object is not being created for some reason and so it can't get it's properties. I cannot figure out why it would not be created though.
I have a jasmine test that is failing, this is the only one failing:
it('should display "Todos" in h1 tag', async(() => {
let fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Todos');
}));
This is my HTML Template:
<section class="todoapp">
<header class="header">
<h1>Todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus="" [(ngModel)]="newTodo.title" (keyup.enter)="addTodo()">
</header>
<section class="main" *ngIf="todos.length > 0">
<ul class="todo-list">
<li *ngFor="let todo of todos" [class.completed]="todo.complete">
<div class="view">
<input class="toggle" type="checkbox" (click)="toggleTodoComplete(todo)" [checked]="todo.complete">
<label>{{todo.title}}</label>
<button class="destroy" (click)="removeTodo(todo)"></button>
</div>
</li>
</ul>
</section>
<footer class="footer" *ngIf="todos.length > 0">
<span class="todo-count"><strong>{{todos.length}}</strong> {{todos.length == 1 ? 'item' : 'items'}} left</span>
</footer>
</section>
This is my class where some of the code may be causing the error?
import { Component } from '@angular/core';
import { Todo } from './todo';
import { TodoDataService } from './todo-data.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [ TodoDataService ]
})
export class AppComponent {
newTodo: Todo = new Todo();
constructor(private todoDataService: TodoDataService) {
}
addTodo() {
this.todoDataService.addTodo(this.newTodo);
this.newTodo = new Todo();
}
toggleTodoComplete(todo) {
this.todoDataService.toggleTodoComplete(todo);
}
removeTodo(todo) {
this.todoDataService.deleteTodoById(todo.id);
}
getTodos() {
return this.todoDataService.getAllTodos();
}
}
This is part of the Jasmine output where I think it's trying to tell me what's wrong.
Failed: Error in ./AppComponent class AppComponent - inline template:5:24 caused by: Cannot read property 'length' of undefined
Error: Error in ./AppComponent class AppComponent - inline template:5:24 caused by: Cannot read property 'length' of undefined
Here is my service code:
import { Injectable } from '@angular/core';
import { Todo } from './todo';
@Injectable()
export class TodoDataService {
// Placeholder for last id so we can simulate
// automatic incrementing of id's
lastId: number = 0;
// Placeholder for todo's
todos: Todo[] = [];
constructor() { }
// Simulate POST /todos
addTodo(todo: Todo): TodoDataService {
if (!todo.id) {
todo.id = ++this.lastId;
}
this.todos.push(todo);
return this;
}
// Simulate DELETE /todos/:id
deleteTodoById(id: number): TodoDataService {
this.todos = this.todos.filter(todo => todo.id !== id);
return this;
}
// Simulate PUT /todos/:id
updateTodoById(id: number, values: Object = {}): Todo {
let todo = this.getTodoById(id);
if (!todo) {
return null;
}
Object.assign(todo, values);
return todo;
}
// Simulate GET /todos
getAllTodos(): Todo[] {
return this.todos;
}
// Simulate GET /todos/:id
getTodoById(id: number): Todo {
return this.todos.filter(todo => todo.id === id).pop();
}
// Toggle todo complete
toggleTodoComplete(todo: Todo) {
let updatedTodo = this.updateTodoById(todo.id, {
complete: !todo.complete
});
return updatedTodo;
}
}
Upvotes: 0
Views: 2086
Reputation: 14564
In your TodoComponent template, you're referring to a todos array. But there isn't a todos array member on that TodoComponent. Hence, in your template, todos will be undefined.
<section class="main" *ngIf="todos.length > 0"> <--- that todos doesn't exist on your component
Now, you have a TodoDataService
that seems to encapsulate your list of todos. So, what makes the most sense is to expose that list of Todos
from your TodoDataService
(via something like a getTodos()
method that returns your list of todos) and then you can use that list of todos in your template.
Upvotes: 3