サイトトップ

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

<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.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


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


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