4. Todo一覧と消化状況を表示する
July 18, 2016 · View on GitHub
Todo一覧と消化状況を表示します。手順は以下の通りです。
- localStorageからTodoを取得する
- Todo表示部品(TodoContentComponent)を作成する
- AppComponentにTodoContentComponentを追加する
- Todo消化状況を取得する
- Todo消化状況表示部品(TodoFooterComponent)を作成する
- AppComponentにTodoFooterComponentを追加する
localStorageからTodoを取得する
まず、localStorageからTodoを取得する機能をServiceクラスに追加します。
:pencil2: app/services/todo.service.ts
import {Injectable} from "@angular/core";
import {Todo} from "../models/todo.model";
const STORAGE_KEY = 'angular2-todo';
@Injectable()
export class TodoService {
todos:Todo[] = [];
constructor() {
+ const persistedTodos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
+ // localStorageの内容をTodoModelクラスに詰め替える
+ this.todos = persistedTodos.map(todo => {
+ return new Todo(
+ todo.id,
+ todo.title,
+ todo.isCompleted
+ );
+ });
}
// ...省略
}
Todo表示部品(TodoContentComponent)を作成する
Todoの一覧を表示するエリアを作成します。Todoが1件も登録されていない場合は、登録されてない旨を表示します。
:pencil2: app/components/content/content.component.ts
import {Component, Input} from '@angular/core';
import {Todo} from "../../models/todo.model";
@Component({
selector: 'todo-content',
templateUrl: 'app/components/content/content.html'
})
export class TodoContentComponent {
@Input()
todos:Todo[];
constructor() {}
}
@Input()は、Component外部から値を注入するためのデコレータです。これが指定されているプロパティは、Component外部から値を受け取ることができます。
:pencil2: app/components/content/content.component.ts
<ul class="list-group">
<li class="list-group-item" *ngFor="let todo of todos; let i = index">
<div class="row">
<div class="col-xs-10">
{{i + 1}}. {{todo.title}}
</div>
<div class="col-xs-2">
<!--ここにあとで削除ボタンを置く-->
</div>
</div>
</li>
<li class="list-group-item text-danger" *ngIf="!todos.length">Todoがありません。</li>
</ul>
*ngForは、Angular1のng-repeatと等価です。*ngIfは、Angular1のng-ifと等価です。
AppComponentにTodoContentComponentを追加する
TodoContentComponentを画面上に表示するためには、AppComponentに登録する必要があります。また、Todoの一覧は後ほど登場するComponentで再利用するため、AppComponent内に保持しておくようにします。
:sparkles: Todo一覧の共有方法はSharedServiceを利用するなど、他にもアプローチの仕方が存在します。
:pencil2: app/app.component.ts
import {Component} from '@angular/core';
import {TodoHeaderComponent} from './header/header.component';
+ import {TodoContentComponent} from "./content/content.component";
import {TodoService} from '../services/todo.service';
import {Todo} from "../models/todo.model";
@Component({
selector: 'my-app',
templateUrl: 'app/components/app.html',
providers: [TodoService],
- directives: [TodoHeaderComponent]
+ directives: [TodoHeaderComponent, TodoContentComponent]
})
export class AppComponent {
+ todos:Todo[];
- constructor() {
+ constructor(private service:TodoService) {
+ this.todos = this.service.todos;
}
}
:pencil2: app/app.html
<section class="container">
<todo-header></todo-header>
+ <todo-content [todos]="todos"></todo-content>
</section>
<footer class="container">
Created by YOUR_NAME
</footer>
[todos]="todos"は、TodoContentComponentのtodos対してAppComponentのtodosを渡します。
Todo消化状況を取得する
次にTodoの消化状況を算出する関数をServiceクラスに作成します。
:pencil2: app/services/todo.service.ts
import {Injectable} from "@angular/core";
import {Todo} from "../models/todo.model";
const STORAGE_KEY = 'angular2-todo';
@Injectable()
export class TodoService {
todos:Todo[] = [];
// ... 省略
+ getComplatedCount():number {
+ return this.todos.filter(todo => todo.isCompleted).length;
+ }
private save():void {
console.log('saving : ', this.todos);
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos));
}
}
Todo消化状況表示部品(TodoFooterComponent)を作成する
Todoの消化状況はFooterに表示することにします。フッターには消化状況とTodoの合計を表示します。
:pencil2: app/components/footer/footer.html
<footer class="container">
<p *ngIf="todos">Todo消化状況: {{getCompletedCount()}} / {{todos.length}}</p>
</footer>
:pencil2: app/components/footer/footer.component.ts
import {Component, Input} from '@angular/core';
import {TodoService} from '../../services/todo.service';
import {Todo} from "../../models/todo.model";
@Component({
selector: 'todo-footer',
templateUrl: 'app/components/footer/footer.html'
})
export class TodoFooterComponent {
@Input()
todos:Todo[];
constructor(private service:TodoService) {}
getCompletedCount() {
return this.service.getComplatedCount();
}
}
AppComponentにTodoFooterComponentを追加する
TodoFooterComponentを画面上に表示するためには、これまでと同様にAppComponentに登録する必要があります。
:pencil2: app/app.component.ts
import {Component} from '@angular/core';
import {TodoHeaderComponent} from './header/header.component';
import {TodoContentComponent} from "./content/content.component";
+ import {TodoFooterComponent} from "./footer/footer.component";
import {TodoService} from '../services/todo.service';
import {Todo} from "../models/todo.model";
@Component({
selector: 'my-app',
templateUrl: 'app/components/app.html',
providers: [TodoService],
- directives: [TodoHeaderComponent, TodoContentComponent]
+ directives: [TodoHeaderComponent, TodoContentComponent, TodoFooterComponent]
})
export class AppComponent {
todos:Todo[];
constructor(private service:TodoService) {
this.todos = this.service.todos;
}
}
:pencil2: app/app.html
<section class="container">
<todo-header></todo-header>
<todo-content [todos]="todos"></todo-content>
+ <todo-footer [todos]="todos"></todo-footer>
</section>
<footer class="container">
Created by YOUR_NAME
</footer>
以上でこの章は終了です。
画面上にTodo一覧と消化状況が表示されていれば、次に進んでください。