サイトトップ

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

HTML5テクニカルノート

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


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

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

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

<body>要素

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

</section>

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

script.js

var app = new Vue({

	computed: {

		allDone: {
			get: function() {
				return this.remaining === 0;
			},
			set: function(value) {
				this.todos.forEach(function(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

var app = new Vue({

	methods: {

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

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

図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"
			@keydown.enter="addTodo">
	</header>
	<section class="main" v-show="todos.length" v-cloak>
		<input class="toggle-all" type="checkbox" v-model="allDone">
		<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

var STORAGE_KEY = 'todos-vuejs-2.0';
var todoStorage = {
	fetch: function() {
		var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
		todos.forEach(function(todo, index) {
			todo.id = index;
		});
		todoStorage.uid = todos.length;
		return todos;
	},
	save: function(todos) {
		localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
	}
};
var filters = {
	all: function(todos) {
		return todos;
	},
	active: function(todos) {
		return todos.filter(function(todo) {
			return !todo.completed;
		});
	},
	completed: function(todos) {
		return todos.filter(function(todo) {
			return todo.completed;
		});
	}
};
var app = new Vue({
	data: {
		todos: todoStorage.fetch(),
		newTodo: '',
		visibility: 'all'
	},
	watch: {
		todos: {
			handler: function(todos) {
				todoStorage.save(todos);
			},
			deep: true
		}
	},
	computed: {
		filteredTodos: function() {
			return filters[this.visibility](this.todos);
		},
		remaining: function() {
			var todos = filters.active(this.todos);
			return todos.length;
		},
		allDone: {
			get: function() {
				return this.remaining === 0;
			},
			set: function(value) {
				this.todos.forEach(function(todo) {
					todo.completed = value;
				});
			}
		}
	},
	filters: {
		pluralize: function(n) {
			return n === 1 ? 'item' : 'items';
		}
	},
	methods: {
		addTodo: function() {
			var value = this.newTodo && this.newTodo.trim();
			if (!value) {
				return;
			}
			this.todos.push({
				id: todoStorage.uid++,
				title: value,
				completed: false
			});
			this.newTodo = '';
		},
		removeTodo: function(todo) {
			this.todos.splice(this.todos.indexOf(todo), 1);
		},
		removeCompleted: function() {
			this.todos = filters.active(this.todos);
		}
	}
});
function onHashChange() {
	var 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: TodoMVC part 04


作成者: 野中文雄
作成日: 2016年7月5日


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