HTML5テクニカルノート
Vue.js + ES6入門 05: 項目を数えて表示する
- ID: FN1906002
- Technique: HTML5 / ECMAScript 2015
- Library: Vue.js 2.6.10
Vue.jsで、条件に合ったデータの数え方を考えます。ひとつのやり方は、メソッドを呼び出して調べることです。もうひとつ、メソッドであってもプロパティのように扱える「算出プロパティ」というオプションがあります。
01 項目の数を示す
「Vue.js + ES6入門 04: フィールドに入力したテキストを動的に項目として加える」でつくったコード001Vueディレクティブの省略記法とECMAScript 2015のスプレッド構文を使うに手を加えてゆきましょう。アプリケーションに納めた複数のデータがリストとして示され、入力フィールドのテキストは「追加」ボタンで項目に加えられます(図001)。これら項目のすべてと処理の済んでいない数を、それぞれ調べてつぎのように表示するのが課題です。
全○件中残り○件
図001■入力フィールドのテキストがページの項目に加えられる
項目のデータは以下のように、配列のプロパティに納められています。ですから、Array.length
プロパティで調べられます。HTMLの要素には、二重波かっこ{{}}
でバインディングすればよいでしょう。これで、項目の総数は示せます(図002)。
<body>要素<div id="app" class="container"> <h2>Todo</h2> <p> 全{{todos.length}}件 </p> </div>
<script>要素const app = new Vue({ data: { todos: [ {text: 'Vue.jsを学ぶ', done: true}, {text: 'Vue.jsでアプリケーションをつくる', done: false}, ] }, });
図002■項目の総数が示された
02 条件に合ったデータを数えて返す
チェックをつけた項目は、データのプロパティ(done
)の値がtrue
になります。その数を調べて返すメソッド(remaining()
)とバインディングしたのが、つぎのコードです。配列要素をすべて取り出す処理には、ECMAScript 5.1で備わったArray.prototype.forEach()
メソッドを用いました。これで、未処理の項目数がページに示され、項目数や処理のチェックを変えるたびに値が改まります(図003)。
<body>要素<div id="app" class="container"> <h2>Todo</h2> <p> 全{{todos.length}}件中残り{{remaining()}}件 </p> </div>
<script>要素const app = new Vue({ methods: { remaining() { let count = 0; this.todos.forEach((todo) => { if(!todo.done) { count++; } }); return count; } } });
図003■項目総数に加えて未処理の件数が示される
03 算出プロパティを使う
Vueインスタンスには、メソッドのほかに「算出プロパティ」が定められます。中身はメソッド(methods
)と同じ関数です。けれど、プロパティのように参照できます(getter関数)。つまり、テンプレートに呼び出しのかっこ()
はつけません。Vue()
コンストラクタに渡す引数オブジェクトのcomputed
オプションに関数として定めてください。
未処理件数を返す前掲メソッド(remaining()
)はそのままcomputed
オプションに移し、テンプレートの二重波かっこ{{}}
内の参照から呼び出しのかっこ()
を除けば同じように動くはずです。けれどこの機会に、同じ処理をつぎのように書き替えてみましょう。用いたメソッドは、ECMAScript 5.1で備わったArray.prototype.filter()
です。引数に渡したコールバック関数が、配列要素を順に受け取ります。そして、true
を返す要素のみを納めた新たな配列が返されるのです。その要素数(Array.length
プロパティ)を調べればよいでしょう。
<body>要素<script>要素<div id="app" class="container"> <p> 全{{todos.length}}件中残り{{remaining}}件 <!-- {{remaining()}}件 --> </p> </div>
const app = new Vue({ computed: { remaining() { /* let count = 0; this.todos.forEach((todo) => { if(!todo.done) { count++; } }); */ const count = this.todos.filter((todo) => !todo.done).length; return count; } }, methods: { /* remaining() { // ↑computedへ } */ } });
メソッドでも算出プロパティでも、得られる結果はまったく同じです。ただし、「算出プロパティは依存関係にもとづきキャッシュされる」という点が異なります。メソッドが呼び出されるのは、ページが描き替わるたびです。それに対して、算出プロパティは、リアクティブな依存関係(今回はtodos)が更新されたときにだけ評価し直されます。依存するデータが変わらなければ、関数は呼び出されず、前に得た戻り値をそのままき返すのです。とくに、処理の負荷が高いときは、それを減らす効果があります。逆に、再描画のたびに処理すべきときは、メソッドを使ってください。また、算出プロパティでは、データの変更が反映されない場合もあります(「Vue.js: v-model.numberでバインディングしたデータに数値のあとに文字をつづけて入力すると算出プロパティが呼び出されない」参照)。
算出プロパティを用いた<body>
要素と<script>
要素の記述は、つぎのコード001のとおりです。
コード001■条件に合ったデータを数えて返す
<body>要素
<div id="app" class="container">
<h2>Todo</h2>
<p>
全{{todos.length}}件中残り{{remaining}}件
</p>
<ul class="list-unstyled">
<li v-for="todo in todos">
<label>
<input type="checkbox" v-model="todo.done">
<span v-bind:class="{'done': todo.done}">{{todo.text}}</span>
</label>
</li>
</ul>
<p>
<input type="text" v-model="todoText" placeholder="add new todo here">
<button v-on:click="addTodo" class="btn btn-primary btn-sm">追加</button>
</p>
</div>
const app = new Vue({
data: {
todoText: '',
todos: [
{text: 'Vue.jsを学ぶ', done: true},
{text: 'Vue.jsでアプリケーションをつくる', done: false},
]
},
computed: {
remaining() {
const count =
this.todos.filter((todo) => !todo.done).length;
return count;
}
},
methods: {
addTodo() {
const newTodo = this.todoText.trim();
this.todoText = '';
if (!newTodo) {return;}
this.todoText = '';
this.todos = [
...this.todos,
{text: newTodo, done: false}
];
}
}
});
document.addEventListener('DOMContentLoaded', () =>
app.$mount('#app')
);
04 Array.prototype.reduce()メソッドで配列を集計する
未処理項目数を調べるのに、前掲コード001ではArray.prototype.filter()
でつくった条件に合う要素の配列から、数を得ました。わかりやすい処理です。けれど、未処理項目の配列そのものは使いません。その意味では、Array.prototype.forEach()
メソッドで数を数える方が端的です。そこでもうひとつ、ECMAScript 5.1のメソッドArray.prototype.reduce()
をご紹介します。
Array.prototype.reduce()
メソッドが返すのはひとつの集計値です。配列要素を順に受け取り、集計した値をつぎの要素の処理に渡します。そうして、ひとつの集計値をまとめたうえで返すということです。メソッド第2引数の初期値を渡さないと、配列の最初の要素が集計値としてつぎの要素のコールバックに渡されます。
構文
配列.reduce(コールバック, 初期値)
- コールバック: 集計値を返す関数
- 引数
- 前の集計値
- 現在の要素
- 戻り値: 集計値
- 初期値: 集計値の初期値(省略可能)
Array.prototype.reduce()
メソッドを用いると、前掲コード001はつぎのように書き替えられます。処理の中身はArray.prototype.forEach()
メソッドを使ったコードと基本的に変わりません。違うのは、カウンタ変数を外に宣言することなく、コールバックの引数として集計値がバケツリレーのように順に渡されることです。最近のArray
クラスのメソッドは、もとの配列を書き替えたりせず、メソッドの処理が外に影響を与えないように定められています。これは関数型プログラミングの考え方です。
<script>要素const app = new Vue({ computed: { remaining() { const count = // this.todos.filter((todo) => !todo.done).length; this.todos.reduce((count, todo) => count = (todo.done) ? count : ++count , 0); return count; } }, });
Array.prototype.reduce()
メソッドに書き替えた<script>要素の記述をまとめたのがつぎのコード002です。併せて、コードを試すための以下のサンプル001をCodePenに掲げました。
コード002■Array.prototype.reduce()メソッドで配列を集計する
<script>要素
const app = new Vue({
data: {
todoText: '',
todos: [
{text: 'Vue.jsを学ぶ', done: true},
{text: 'Vue.jsでアプリケーションをつくる', done: false},
]
},
computed: {
remaining() {
const count =
this.todos.reduce((count, todo) =>
count = (todo.done) ? count : ++count
, 0);
return count;
}
},
methods: {
addTodo() {
const newTodo = this.todoText.trim();
this.todoText = '';
if (!newTodo) {return;}
this.todos = [
...this.todos,
{text: newTodo, done: false}
];
}
}
});
document.addEventListener('DOMContentLoaded', () =>
app.$mount('#app')
);
サンプル001■Vue.js + ES6: Using Computed Properties
See the Pen Vue.js + ES6: Using Computed Properties 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月2日 FN1702008「Vue.js入門 05: 項目を数えて表示する」を全面改訂。
Copyright © 2001-2017 Fumio Nonaka. All rights reserved.