HTML5テクニカルノート
Vue.js + Vuex入門 05: チェックをまとめてオン/オフする
- ID: FN1910004
- Technique: HTML5 / ECMAScript 2015
- Library: Vue.js 2.6.10
単一ファイルコンポーネントにVuexのStoreを加えてつくるTodoMVCアプリケーションのチュートリアルシリーズ「Vue.js + Vuex入門」の第5回で加えるのは、リスト項目チェックをまとめてオン/オフできる機能です。そしてもうひとつ、Storeのデータを算出プロパティに定めるためのヘルパー関数もご紹介します。
01 全項目のチェック切り替えサインを表示する
まず、リスト項目の処理済みチェックを、まとめてオン/オフできるようにしましょう。リスト表示のコンポーネント(src/components/TodoList.vue
)には、先頭にまだ使っていないチェックボックスの<input type="checkbox">
要素がすでに加えてありました。そして、<input type="checkbox">
要素には、id
属性が与えられています。対応する<label>
要素を加えると、index.css
によりチェックサインが表れるはずです(図001参照)。
このチェックボックス(<input type="checkbox">
)をモジュールsrc/store.js
のgetters
に新たに定めるメソッド(allDone
)と、つぎのようにデータバインディングします。戻り値は、すべての項目が処理済みだとtrue
となるブール(論理)値です。checked
属性にもバインディングするのを忘れないでください。
src/components/TodoList.vue<template> <section class="main" v-show="todos.length" v-cloak> <input id="toggle-all" class="toggle-all" type="checkbox" :value="allDone" :checked="allDone" > <label for="toggle-all" /> </section> </template> <script> export default { computed: { allDone() { return this.$store.getters.allDone; } } }; </script>
src/store.jsexport default new Vuex.Store({ getters: { allDone: (state, getters) => getters.remaining === 0 }, });
チェックサインのオン/オフを切り替えたときの処理は、このあと加えます。けれど、スタイル(クラスtoggle-all
)が与えてあるので(index.css
参照)、すべての項目をチェックすると、checked
属性にバインディングした値(allDone
)はtrue
になり、アクティブな表示に変わります。
図001■全項目を切り替えるチェックサイン
02 全項目のチェックをまとめて切り替える
前項ですべての項目を処理済みにチェックしたとき、チェックボックス(<input type="checkbox">
)の値(value
)と要素のスタイルが変わるようにしました。本項では、チェックボックスのオン/オフで、全項目のチェックをまとめて切り替えるようにしましょう。そのためには、コンポーネントsrc/components/TodoList.vue
の算出プロパティ(allDone
)の値を変えなければなりません。算出プロパティは、デフォルトでは読み取り専用です。けれど、Setter関数が加えられます(「算出Setter関数」参照)。
算出プロパティ(allDone
)が返すのは、モジュールsrc/store.js
に定めたgetters
の同名メソッドへの参照です。そこで、mutations
に加える新たなメソッド(setAllDone()
)で、つぎのように戻り値を変えます。算出Setter関数を呼び出すのは、コンポーネントsrc/components/TodoList.vue
のチェックボックス(<input type="checkbox">
)のchange
イベントに加えるリスナーメソッド(onInput()
)です。処理の済んでいない項目があればすべてを処理済みにし、全項目処理が終わっていたら逆にすべて未処理に戻します。
src/components/TodoList.vue<template> <section class="main" v-show="todos.length" v-cloak> <input @change="onInput" > </section> </template> <script> export default { computed: { // allDone() { allDone: { get() { return this.$store.getters.allDone; }, set(value) { this.$store.commit('setAllDone', value); } } }, methods: { onInput() { this.allDone = !this.allDone; } } }; </script>
src/store.jsexport default new Vuex.Store({ mutations: { setAllDone(state, value) { state.todos.forEach((todo) => todo.completed = value ); } } });
これですべての項目のチェックを切り替えるチェックサインができ上がりました(図002)。コンポーネントsrc/components/TodoList.vue
とモジュールsrc/store.js
の記述は、以下のコード001にまとめたとおりです。
図002■全項目のチェックが変えられる
コード001■全項目のチェック切り替えサインが加わったリスト表示コンポーネントとStoreモジュール
src/components/TodoList.vue
<template>
<section class="main" v-show="todos.length" v-cloak>
<input
id="toggle-all"
class="toggle-all"
type="checkbox"
:value="allDone"
:checked="allDone"
@change="onInput"
>
<label for="toggle-all" />
<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;
},
allDone: {
get() {
return this.$store.getters.allDone;
},
set(value) {
this.$store.commit('setAllDone', value);
}
}
},
methods: {
onInput() {
this.allDone = !this.allDone;
}
}
};
</script>
<style scoped>
[v-cloak] {
display: none;
}
</style>
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const STORAGE_KEY = 'todos-vuejs-2.6';
const todoStorage = {
fetch() {
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
todos.forEach(function(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
);
}
};
export default new Vuex.Store({
state: {
todos: todoStorage.fetch(),
visibility: 'all'
},
getters: {
filteredTodos: (state) =>
filters[state.visibility](state.todos),
remaining: (state) => {
const todos = state.todos.filter((todo) => !todo.completed);
return todos.length;
},
filters: (state) => filters,
allDone: (state, getters) => getters.remaining === 0
},
mutations: {
addTodo(state, todoTitle) {
const newTodo = todoTitle && todoTitle.trim();
if (!newTodo) {
return;
}
state.todos.push({
id: todoStorage.uid++,
title: newTodo,
completed: false
});
},
removeTodo(state, todo) {
state.todos = state.todos.filter((item) => item !== todo);
},
done(state, {todo, completed}) {
state.todos = state.todos.map((item) => {
if(item === todo) {
item.completed = completed
}
return item;
});
},
save(state) {
todoStorage.save(state.todos);
},
hashChange(state) {
const visibility = window.location.hash.replace(/#\/?/, '');
if (filters[visibility]) {
state.visibility = visibility;
}
},
setAllDone(state, value) {
state.todos.forEach((todo) =>
todo.completed = value
);
}
}
});
03 mapState()ヘルパー関数を使う
つぎに、Vuexのヘルパー関数をご紹介しましょう。mapState()
関数は戻り値が、算出プロパティに定めるメソッドを収めたオブジェクトです。引数には算出プロパティとするメソッドを渡します(「mapStateヘルパー」参照)。ただし、引数にStoreのstate
を受け取るので、this.$store.state
の参照が要りません。もうひとつ注意しなければならないのは、オブジェクトが返されるため、他にも算出プロパティがあるときは、メソッドを取り出して展開しなければならないことです。そのため、つぎのようにスプレッド構文...
を用います(「スプレッド構文を使う」参照)。
src/components/TodoController.vueimport {mapState} from 'vuex'; export default { computed: { /* todos() { return this.$store.state.todos; }, */ ...mapState({ todos: (state) => state.todos, visibility: (state) => state.visibility }), /* visibility() { return this.$store.state.visibility; } */ } }
04 mapGetters()ヘルパー関数を使う
mapGetters()
ヘルパー関数が返すのも、算出プロパティに定めるメソッドを収めたオブジェクトです(「mapGettersヘルパー」参照)。ただし、Storeのgetters
の参照をそのまま返します。そのため、引数は配列で、要素は参照するgetters
の名前の文字列です。算出プロパティ名を変えたいときは、引数はオブジェクトにして、新しい名前のプロパティにgetters
の文字列を値にすることもできます。今回は、getters
の参照をそのまま返せばよいので、引数はつぎのように配列にしました。やはりメソッドは展開しなければならないので、スプレッド構文...
を忘れないでください。
src/components/TodoController.vueimport {mapState, mapGetters} from 'vuex'; export default { computed: { /* remaining() { return this.$store.getters.remaining; }, filters() { return this.$store.getters.filters; }, */ ...mapGetters([ 'remaining', 'filters' ]) } }
05 mapState()関数の簡易な構文
mapState()
関数はstate
の参照から取り出した値をさまざまに加工できます。けれども、コンポーネントsrc/components/TodoController.vue
では、値の参照をただ返しているだけです。こういうときは、mapGetters()
の構文と同じく、引数にプロパティ名の文字列を要素にした配列が渡せます。
src/components/TodoController.vueimport {mapState, mapGetters} from 'vuex'; export default { computed: { /* ...mapState({ todos: (state) => state.todos, visibility: (state) => state.visibility }), */ ...mapState([ 'todos', 'visibility' ]), } }
これでコンポーネントsrc/components/TodoController.vue
もでき上がりです(コード002)。CodeSandboxに公開した以下のサンプル001で、各モジュールのコードと動きがお確かめいただけます。
コード002■フッタのコンポーネントにヘルパー関数mapState()とmapGetters()を使う
src/components/TodoController.vue
<template>
<footer class="footer" v-show="todos.length" v-cloak>
<span class="todo-count">
<strong>{{remaining}}</strong> {{remaining | pluralize}} left
</span>
<ul class="filters">
<li v-for="(value, key) in filters" :key="key">
<a
:href="'#/' + key"
:class="{selected: visibility === key}"
>
{{ key[0].toUpperCase() + key.substr(1) }}
</a>
</li>
</ul>
</footer>
</template>
<script>
import {mapState, mapGetters} from 'vuex';
export default {
name: 'TodoController',
filters: {
pluralize(n) {
return n === 1 ? 'item' : 'items';
}
},
computed: {
...mapState([
'todos',
'visibility'
]),
...mapGetters([
'remaining',
'filters'
])
}
};
</script>
サンプル001■vue-vuex-todo-mvc-05
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年10月21日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.