HTML5テクニカルノート
Vue.js + ES6入門 08: フィルタをボタンで切り替える
- ID: FN1906005
- Technique: HTML5 / ECMAScript 2015
- Library: Vue.js 2.6.10
「Vue.js + ES6入門 07: 表示する項目をフィルタで切り替える」は、データのフィルタにより、処理済みと未処理およびすべての項目を切り替え表示できるようにしました。今回、基本的な機能は変えません。フィルタの切り替えを、リンクのハッシュでなく、ボタンで行うようにしてみます。
01 フィルタの切り替えの設定を確かめる
「Vue.js + ES6入門 07」で書いたコード002「フィルタで表示する項目を切り替える」は、<a>
要素に加えたhref
属性のハッシュ(#
)により、フィルタを切り替えました。これはVue.js公式サイトの「TodoMVCの例」が用いた手法です。けれど、ボタンにイベントリスナーを定める方がすっきりします。
また、フィルタを決めるキーワード(all
/active
/completed
)が、ハッシュとクラスバインディング、およびリンクボタンの名前に手打ちで書かれています。これらは自動で一致させたいところです。
<body>要素<div id="app" class="container"> <a href="#/all" :class="['btn btn-outline-info btn-sm', {active: visibility === 'all'}]">All</a> <a href="#/active" :class="['btn btn-outline-info btn-sm', {active: visibility === 'active'}]">Active</a> <a href="#/completed" :class="['btn btn-outline-info btn-sm', {active: visibility === 'completed'}]">Completed</a> </div>
さらにキーワードは、フィルタのオブジェクト(filters
)にも、つぎのようにメソッドとして備わっています。そこで、このメソッド名をフィルタボタンのタグの設定に利用することにしましょう。
<script>要素const filters = { all(todos) { }, active(todos) { }, completed(todos) { } };
02 選択したボタンのスタイルをクラスバインディングで変える
テンプレートでオブジェクトからプロパティを取り出すのは、v-for
ディレクティブです。第1引数(value
)は値、そして第2引数(key
)にプロパティ名が受け取れます(「オブジェクトのv-for」参照)。これを、つぎのように<button>
要素の属性の定めに用いればよいでしょう。クリックイベント(@click
)のハンドラメソッド(changeFilter()
)にも、このプロパティ名を渡します。なお、文字列にブラケット記法[]
でインデックスから文字を得るのはECMAScript 5の構文です。
<body>要素<div id="app" class="container"> <!-- <a href="#/all" :class="['btn btn-outline-info btn-sm', {active: visibility === 'all'}]">All</a> <a href="#/active" :class="['btn btn-outline-info btn-sm', {active: visibility === 'active'}]">Active</a> <a href="#/completed" :class="['btn btn-outline-info btn-sm', {active: visibility === 'completed'}]">Completed</a> --> <button type="button" v-for="(value, key) in filters" :class="['btn btn-outline-info btn-sm mr-1', {active: visibility === key}]" @click="changeFilter(key)"> {{ key[0].toUpperCase() + key.substr(1) }} </button> </div>
ボタンクリックのイベントハンドラ(changeFilter()
)は、引数に受け取ったキーワードをプロパティ(visibility
)に与えるだけです。ハッシュ切り替えのメソッド(onHashChange()
)とイベントリスナーの設定(mounted()
)は要らなくなります。
<script>要素const app = new Vue({ /* mounted() { window.addEventListener('hashchange', this.onHashChange); this.onHashChange(); }, */ methods: { /* onHashChange() { const visibility = window.location.hash.replace(/#\/?/, ''); if (filters[visibility]) { this.visibility = visibility; } else { window.location.hash = ''; this.visibility = 'all'; } } */ changeFilter(visibility) { this.visibility = visibility; } } });
フィルタのボタンをクリックすると、表示される項目がこれまでと同じように切り替わります(図001))。ひとつ違うのは、URLにハッシュが残らないことです。そのため、ページを読み込み直すと、フィルタボタンの選択は初期値(All)に戻ります。URLも切り替えたい場合には、ルーティングを考えた方がよいでしょう。
図001■フィルタボタンで表示される項目が切り替わる
書き改めたHTMLとJavaScriptの記述は、つぎのコード001のとおりです。コードを試すためのサンプル001も併せて掲げます。
コード001■フィルタをボタンで切り替える
<body>要素
<div id="app" class="container">
<h2>Todo</h2>
<p>
全{{todos.length}}件中残り{{remaining}}件
<button @click="archive" class="btn btn-danger btn-sm">断捨離</button>
</p>
<ul class="list-unstyled">
<li v-for="todo in filteredTodos">
<label>
<input type="checkbox" v-model="todo.done">
<span :class="{'done': todo.done}">{{todo.text}}</span>
</label>
<button @click="removeTodo(todo)" class="btn btn-warning btn-sm">削除</button>
</li>
</ul>
<p>
<input type="text" v-model="todoText" placeholder="add new todo here">
<button @click="addTodo" class="btn btn-primary btn-sm">追加</button>
</p>
<button type="button"
v-for="(value, key) in filters"
:class="['btn btn-outline-info btn-sm mr-1', {active: visibility === key}]"
@click="changeFilter(key)">
{{ key[0].toUpperCase() + key.substr(1) }}
</button>
</div>
const filters = {
all(todos) {
return todos;
},
active(todos) {
return todos.filter((todo) =>
!todo.done
);
},
completed(todos) {
return todos.filter((todo) =>
todo.done
);
}
};
const app = new Vue({
data: {
todoText: '',
todos: [
{text: 'Vue.jsを学ぶ', done: true},
{text: 'Vue.jsでアプリケーションをつくる', done: false},
],
visibility: 'all'
},
computed: {
remaining() {
const count =
this.todos.reduce((count, todo) =>
count = (todo.done) ? count : ++count
, 0);
return count;
},
filteredTodos() {
return filters[this.visibility](this.todos);
}
},
methods: {
addTodo() {
const newTodo = this.todoText.trim();
this.todoText = '';
if (!newTodo) {return;}
this.todos = [
...this.todos,
{text: newTodo, done: false}
];
},
removeTodo(todo) {
this.todos = this.todos.filter((_todo) => _todo !== todo);
},
archive() {
this.todos = this.todos.filter((todo) => !todo.done);
},
changeFilter(visibility) {
this.visibility = visibility;
}
}
});
document.addEventListener('DOMContentLoaded', () =>
app.$mount('#app')
);
サンプル001■Vue.js + ES6: Filtering items to list with buttons
See the Pen Vue.js + ES6: Filtering items to list with buttons by Fumio Nonaka (@FumioNonaka) on CodePen.
Vue.js + ES6
- Vue.js + ES6入門 01: Vue.jsを始める
- Vue.js + ES6入門 02: 要素のclass属性を動的に変える
- Vue.js + ES6入門 03: データから動的にリストをつくる
- Vue.js + ES6入門 04: フィールドに入力したテキストを動的に項目として加える
- Vue.js + ES6入門 05: 項目を数えて表示する
- Vue.js + ES6入門 06: 項目を調べてデータから削除する
- Vue.js + ES6入門 07: 表示する項目をフィルタで切り替える
- Vue.js + ES6入門 08: フィルタをボタンで切り替える
- Vue.js + ES6入門 09: アプリケーションをコンポーネントに分ける
- Vue.js + ES6入門 10: コンポーネントの応用とデータのチェック
- Vue.js + ES6入門 11: データチェックの応用とkey属性
- Vue.js + ES6入門 12: ローカルコンポーネントを定める
作成者: 野中文雄
作成日: 2019年6月24日
Copyright © 2001-2019 Fumio Nonaka. All rights reserved.