サイトトップ

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

HTML5テクニカルノート

Vue.js + ES6入門 05: 項目を数えて表示する


Vue.jsで、条件に合ったデータの数え方を考えます。ひとつのやり方は、メソッドを呼び出して調べることです。もうひとつ、メソッドであってもプロパティのように扱える「算出プロパティ」というオプションがあります。

01 項目の数を示す

「Vue.js + ES6入門 04: フィールドに入力したテキストを動的に項目として加える」でつくったコード001Vueディレクティブの省略記法とECMAScript 2015のスプレッド構文を使うに手を加えてゆきましょう。アプリケーションに納めた複数のデータがリストとして示され、入力フィールドのテキストは「追加」ボタンで項目に加えられます(図001)。これら項目のすべてと処理の済んでいない数を、それぞれ調べてつぎのように表示するのが課題です。

全○件中残り○件

図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■項目の総数が示された

図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■項目総数に加えて未処理の件数が示される

図003

03 算出プロパティを使う

Vueインスタンスには、メソッドのほかに「算出プロパティ」が定められます。中身はメソッド(methods)と同じ関数です。けれど、プロパティのように参照できます(getter関数)。つまり、テンプレートに呼び出しのかっこ()はつけません。Vue()コンストラクタに渡す引数オブジェクトのcomputedオプションに関数として定めてください。

未処理件数を返す前掲メソッド(remaining())はそのままcomputedオプションに移し、テンプレートの二重波かっこ{{}}内の参照から呼び出しのかっこ()を除けば同じように動くはずです。けれどこの機会に、同じ処理をつぎのように書き替えてみましょう。用いたメソッドは、ECMAScript 5.1で備わったArray.prototype.filter()です。引数に渡したコールバック関数が、配列要素を順に受け取ります。そして、trueを返す要素のみを納めた新たな配列が返されるのです。その要素数(Array​.lengthプロパティ)を調べればよいでしょう。

<body>要素

<div id="app" class="container">

	<p>
		全{{todos.length}}件中残り{{remaining}}<!-- {{remaining()}}件 -->

	</p>

</div>

<script>要素

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)が更新されたときにだけ評価し直されます。依存するデータが変わらなければ、関数は呼び出されず、前に得た戻り値をそのままき返すのです。とくに、処理の負荷が高いときは、それを減らす効果があります。逆に、再描画のたびに処理すべきときは、メソッドを使ってください。

算出プロパティを用いた<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>

<script>要素

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;
			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;
			this.todoText = '';
			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


作成者: 野中文雄
作成日: 2019年6月2日 FN1702008「Vue.js入門 05: 項目を数えて表示する」を全面改訂。


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