HTML5テクニカルノート
Vue.js + Vuex入門 02: クラスバインディングと項目の削除
- ID: FN1909003
- Technique: HTML5 / ECMAScript 2015
- Library: Vue.js 2.6.10
「Vue.js + Vuex入門 01: Storeを使う」では、項目を追加してリストに表示できるようにしました。今回は、ふたつ機能を加えます。ひとつは、チェック済み項目のスタイルを変えること。もうひとつは、項目ごとに削除ができるようにすることです。
01 クラスをバインディングする
データに応じてclass
属性の定めを変えるのが「クラスバインディング」です。まず、項目が済んだかどうかのデータ(completed
)を、チェックボックス(<input type="checkbox"
要素)の値にバインディングします(:value
ディレクティブ)。その逆に、チェックボックスが切り替えられたとき(@input
イベント)は、メソッドからmutations
(done()
)の呼び出しによりデータを変更するのです。
このとき、mutations
のメソッドには、切り替える項目のオブジェクト(todo
)と新たな値(completed
)を送らなければなりません。ところが、mutations
のメソッドが受け取るのは、第2引数までです。そのため、複数の値を渡したいときは、第2引数(「ペイロード」と呼ばれます)はオフジェクトのかたちにしてください(「追加の引数を渡してコミットする」参照)。なお、src/store.js
モジュールに定めたつぎのmutations
のメソッド(done()
)は、第2引数でオブジェクトの分割代入によりふたつの値を取り出しています。
src/components/TodoItem.vue<template> <div class="view"> <input type="checkbox" class="toggle" :value="todo.completed" @input="onInput" > </div> </template> <script> export default { methods: { onInput() { this.$store.commit('done', { todo: this.todo, completed: !this.todo.completed }); } } }; </script>
src/store.jsexport default new Vuex.Store({ mutations: { done(state, {todo, completed}) { todo.completed = completed; } } });
クラスバインディングは、ディレクティブ:class
にオブジェクトのかたちでCSSのクラス(completed
)を与えます(「クラスとスタイルのバインディング」参照)。もっとも、スタイルを加えたい要素(<li>
)には、class
属性がすでに含まれていました。重複してよいのでしょうか。結論からいえば、構いません。クラスバインディングはclass
属性を上書きしないからです(「クラスが複数与えられた場合」参照)。
src/components/TodoList.vue<li v-for="todo in filteredTodos" class="todo" :class="{completed: todo.completed}" > </li>
とはいえ、ひとつにまとめた方がわかりやすいでしょう。このようなときに用いるのが「配列構文」です。つねに与えてよいクラスは、配列に文字列の要素として加えます[*01]。データにバインディングするクラスは、オブジェクトのかたちで要素にしてください。これで、処理済みのチェックをした項目は、スタイルが変わります(図001)。
src/components/TodoList.vue<template> <section class="main" v-show="todos.length" v-cloak> <input class="toggle-all" type="checkbox"> <ul class="todo-list"> <li v-for="todo in filteredTodos" :class="['todo', {completed: todo.completed}]" > </li> </ul> </section> </template>
[*01] ただ、index.cssには該当するクラス(todo
)は定められていないようです。したがって、このclass
属性は除いてしまっても構いません。
図001■チェックした項目のスタイルが変わる
02 項目ごとにボタンで削除する
項目のテンプレートには、すでに削除のための<button>
要素が加えてあります。それぞれの項目にロールオーバーすると、CSSで右端に[×]ボタンが表れるはずです(図002)。
図002■項目にロールオーバーすると表れる削除の[×]ボタン
コンポーネントsrc/components/TodoItem.vue
に、ボタンクリックで呼び出すメソッド(removeTodo()
)を新たに定めます。モジュールsrc/store.js
のmutations
に加えたメソッド(removeTodo()
)にcommit()
を送り、第2引数に渡すのは削除すべき項目オブジェクト(todo
)の参照です。
src/components/TodoItem.vue<template> <div class="view"> <button @click="removeTodo"> </button> </div> </template> <script> export default { methods: { removeTodo() { this.$store.commit('removeTodo', this.todo); }, } }; </script>
src/store.jsexport default new Vuex.Store({ mutations: { removeTodo(state, todo) { state.todos = state.todos.filter((item) => item !== todo); }, } });
これで、処理済み項目へのクラスバインディングと項目ごとの削除ができるようになりました。手を加えた4つのモジュールのの中身は、つぎのコード001に掲げます。実際にコードを確かめたい方は、CodeSandboxに公開した以下のサンプル001をお試しください。
コード001■クラスバインディングと項目ごとの削除
src/store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
todos: [],
uid: 0
},
getters: {
filteredTodos: (state) => state.todos
},
mutations: {
addTodo(state, todoTitle) {
const newTodo = todoTitle && todoTitle.trim();
if (!newTodo) {
return;
}
state.todos.push({
id: state.uid++,
title: newTodo,
completed: false
});
},
removeTodo(state, todo) {
state.todos = state.todos.filter((item) => item !== todo);
},
done(state, {todo, completed}) {
todo.completed = completed;
}
}
});
<template>
<section class="main" v-show="todos.length" v-cloak>
<input class="toggle-all" type="checkbox">
<ul class="todo-list">
<li
v-for="todo in filteredTodos"
:class="['todo', {completed: todo.completed}]"
:key="todo.id"
>
<todo-item
:todo="todo" />
</li>
</ul>
</section>
</template>
<script>
import TodoItem from './TodoItem.vue';
export default {
name: 'TodoList',
components: {
TodoItem
},
computed: {
todos() {
return this.$store.state.todos;
},
filteredTodos() {
return this.$store.getters.filteredTodos;
}
}
};
</script>
<style scoped>
[v-cloak] {
display: none;
}
</style>
<template>
<div class="view">
<input
type="checkbox" class="toggle"
:value="todo.completed"
@input="onInput"
>
<label>{{todo.title}}</label>
<button
class="destroy"
@click="removeTodo">
</button>
</div>
</template>
<script>
export default {
name: 'TodoItem',
props: {
todo: Object
},
methods: {
removeTodo() {
this.$store.commit('removeTodo', this.todo);
},
onInput() {
this.$store.commit('done', {
todo: this.todo,
completed: !this.todo.completed
});
}
}
};
</script>
サンプル001■vue-vuex-todo-mvc-02
Vue.js + Vuex入門
- Vue.js + Vuex入門 01: Storeを使う
- Vue.js + Vuex入門 02: クラスバインディングと項目の削除
- Vue.js + Vuex入門 03: データの変化に応じた処理とローカルへの保存
- Vue.js + Vuex入門 04: リスト表示する項目を選び出して切り替える
- Vue.js + Vuex入門 05: チェックをまとめてオン/オフする
- Vue.js + Vuex入門 06: チェックした項目をまとめて削除する
- Vue.js + Vuex入門 07: 項目のテキストをダブルクリックで再編集する
- Vue.js + Vuex入門 08: フォーカスをコントロールする
作成者: 野中文雄
作成日: 2019年09月24日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.