サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

Vue.js + ES6: TodoMVCをつくる 04 ー チェックをまとめてオン/オフしたり削除する


Vue.js + ES6: TodoMVCをつくる 03 ー 表示する項目をフィルタで切り替える」(以下「Vue.js + ES6: TodoMVCをつくる 03」)でつくった作例に、項目がまとめて扱える機能をふたつ加えます。ひとつは、終了済みのチェックを、すべての項目にまとめてつけたり消したりするチェックボックスです。もうひとつは、チェック済みの項目をボタンクリックですべてリストから除けるようにします。

01 項目のチェックをまとめてオン/オフする

リストの項目が済んだかどうかのチェックを、まとめてオン/オフできるようにしましょう。チェックボックスの要素<input>(type属性"checkbox")と<label>を、つぎのように加え、v-modelディレクティブに算出プロパティ(allDone)を与えます。

<body>要素

<section class="main" v-show="todos.length" v-cloak>
	<input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
	<label for="toggle-all"></label>

</section>

算出プロパティに直に定めた関数は、値を返すgetter関数として扱われます。けれど、オブジェクトにしてメソッドgetsetを書き加えれば、前者がgetter、後者はsetterになります(「算出 Setter 関数」参照)。つぎのように加えた算出プロパティ(allDone)は、残り項目(チェックなし)があるかどうかをブール(論理)値で返し、チェックボックスに設定された値(value)をリスト(todos)のすべての項目のプロパティ(completed)に定めます。

script.js

const app = new Vue({

	computed: {

		allDone: {
			get() {
				return this.remaining === 0;
			},
			set(value) {
				this.todos.forEach((todo) =>
					todo.completed = value
				);
			}
		}
	},

});

チェックボックス(class属性"toggle-all")は、CSSでテキスト入力フィールドの左に置かれ、チェック印がつきます(図001)。このオン/オフで項目すべてのチェックがまとめて切り替えられるのです。

図001■上部のチェックボックスですべての項目のチェックがまとめて変えられる

図001

02 チェック済みの項目すべてをリストから除く

チェック済みの項目すべてをリストから除くボタン(<button>要素)は、つぎのようにフッター(<footer>要素に加えます。v-on:click(省略記法@click)イベントにはメソッド(removeCompleted)を定めました。また、v-showディレクティブは、全リスト項目数より残り(チェックなし)項目数(算出プロパティremaining)が少ないことを条件にしているので、チェックされている項目がなければ表示されません。

<body>要素

<footer class="footer" v-show="todos.length" v-cloak>

	<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
		Clear completed
	</button>
</footer>

v-on:clickイベントのリスナーメソッド(removeCompleted())はつぎのように定めます。フィルタ(filters)のメソッド(active())で、チェックのない項目の配列を得て、項目リストのプロパティ(todos)に上書きします。これで、リストからチェック済みの項目がなくなるわけです。

script.js

const app = new Vue({

	methods: {

		removeCompleted() {
			this.todos = filters.active(this.todos);
		}
	}
});

これで、チェック済みの項目をボタンクリックでまとめてリストから除けるようになりました(図002)。すべての項目にチェックをつけたり消したりするチェックボックスと合わせて、ふたつの機能が備わったのです。以下のコード001に、<body>要素とJavaScriptファイルの記述をまとめました。サンプル001にはCodePenのコードを掲げてあります。

図002■チェックした項目がまとめて除かれる

図002

コード001■リストに表示される項目がフィルタで切り替わる

<body>要素

<section class="todoapp">
	<header class="header">
		<h1>todos</h1>
		<input class="new-todo"
			autofocus
			autocomplete="off"
			placeholder="What needs to be done?"
			v-model="newTodo"
			@keypress.enter="addTodo">
	</header>
	<section class="main" v-show="todos.length" v-cloak>
		<input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
		<label for="toggle-all"></label>
		<ul class="todo-list">
			<li v-for="todo in filteredTodos"
				class="todo"
				:key="todo.id"
				:class="{completed: todo.completed}">
				<div class="view">
					<input class="toggle" type="checkbox" v-model="todo.completed">
					<label>{{todo.title}}</label>
					<button class="destroy" @click="removeTodo(todo)"></button>
				</div>
			</li>
		</ul>
	</section>
	<footer class="footer" v-show="todos.length" v-cloak>
		<span class="todo-count">
			<strong>{{remaining}}</strong> {{remaining | pluralize}} left
		</span>
		<ul class="filters">
			<li><a href="#/all" :class="{selected: visibility == 'all'}">All</a></li>
			<li><a href="#/active" :class="{selected: visibility == 'active'}">Active</a></li>
			<li><a href="#/completed" :class="{selected: visibility == 'completed'}">Completed</a></li>
		</ul>
		<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
			Clear completed
		</button>
	</footer>
</section>

script.js

const STORAGE_KEY = 'todos-vuejs-2.0';
const todoStorage = {
	fetch() {
		const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
		todos.forEach((todo, index) =>
			todo.id = index
		);
		todoStorage.uid = todos.length;
		return todos;
	},
	save(todos) {
		localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
	}
};
const filters = {
	all(todos) {
		return todos;
	},
	active(todos) {
		return todos.filter((todo) =>
			!todo.completed
		);
	},
	completed(todos) {
		return todos.filter((todo) =>
			todo.completed
		);
	}
};
const app = new Vue({
	data: {
		todos: todoStorage.fetch(),
		newTodo: '',
		visibility: 'all'
	},
	watch: {
		todos: {
			handler(todos) {
				todoStorage.save(todos);
			},
			deep: true
		}
	},
	computed: {
		filteredTodos() {
			return filters[this.visibility](this.todos);
		},
		remaining() {
			const todos = filters.active(this.todos);
			return todos.length;
		},
		allDone: {
			get() {
				return this.remaining === 0;
			},
			set(value) {
				this.todos.forEach((todo) =>
					todo.completed = value
				);
			}
		}
	},
	filters: {
		pluralize(n) {
			return n === 1 ? 'item' : 'items';
		}
	},
	methods: {
		addTodo() {
			const value = this.newTodo && this.newTodo.trim();
			if (!value) {
				return;
			}
			this.todos.push({
				id: todoStorage.uid++,
				title: value,
				completed: false
			});
			this.newTodo = '';
		},
		removeTodo(todo) {
			this.todos.splice(this.todos.indexOf(todo), 1);
		},
		removeCompleted() {
			this.todos = filters.active(this.todos);
		}
	}
});
function onHashChange() {
	const visibility = window.location.hash.replace(/#\/?/, '');
	if (filters[visibility]) {
		app.visibility = visibility;
	} else {
		window.location.hash = '';
		app.visibility = 'all';
	}
}
window.addEventListener('hashchange', onHashChange);
onHashChange();
app.$mount('.todoapp');

サンプル001■Vue.js + ES6: Vue TodoMVC 04

See the Pen Vue.js + ES6: Vue TodoMVC 04 by Fumio Nonaka (@FumioNonaka) on CodePen.


作成者: 野中文雄
更新日: 2019年11月14日 構文をECMAScript 2015に改め、本文も修正。サンプルはCodePenに差し替えた。
作成日: 2017年07月05日


Copyright © 2001-2019 Fumio Nonaka.  All rights reserved.