繰り返しで長いコードをVue.jsのv-forでスッキリと短く

Vue.jsプログラミング備忘録
https://jp.vuejs.org/からの画像

どうも、ちゃんカマでございます。

同じコードの繰り返しで長くなってしまったHTMLを、短くスッキリと書く方法についてお調べですか?
それならVue.jsのv-forを使うと良いですよ。

私は点数計算を行うWebアプリを制作する過程で、Vue.jsのv-forを使用しました。
その結果、同じコードの繰り返しで長かったHTMLを、短くスッキリと書くことが出来ました。

今回はVue.jsのv-forを使って、長くなったHTMLコードを短くスッキリと書く方法について解説します。

ちなみに私が作ったアプリはこんな感じです。( ↓ GIF動画参照)

点数計算アプリの動作の様子
点数計算アプリの動作の様子

Excelの表計算のようなアプリなので、同じコードを繰り返しで書いています。
そのためVue.jsのv-forを使う前は、HTMLのコードがとても長いものとなっていました。

この記事ではこんな疑問や悩みにお答えします
  • Vue.jsのv-forの使用方法を知りたい
  • 長くなったコードをスッキリさせたい
この記事の内容
  • Vue.jsのv-forの使用方法について
  • dataを配列で指定する方法について
  • 完成コードも記載しています
スポンサーリンク

v-forを使う前のコード【同じコードの繰り返しで長くなってしまった】

v-forを使用する前のコードを下記に記載します。
HTMLが同じコードの繰り返しで長くなっています。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>

	<!-- Normalize CSS -->
	<link rel="stylesheet" href="normalize.css">

	<!-- Bootstrap CSS -->
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">

	<!-- Style CSS -->
	<link rel="stylesheet" href="style.css">

	<!-- Vue.js(本番用) -->
	<!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script> -->

	<!-- Vue.js(開発用) -->
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
	<table class="table table-bordered table-striped">
		<thead class="thead-light">
			<tr>
				<th class="col-sm"></th>
				<th class="col-lg">ゲート1</th>
				<th class="col-lg">ゲート2</th>
				<th class="col-lg">ゲート3</th>
				<th class="col-lg">ゲート4</th>
				<th class="col-lg">ゴール</th>
				<th class="col-md">総打数</th>
				<th class="col-md">加算点</th>
				<th class="col-md">Total</th>
			</tr>
		</thead>
		<tbody class="row-item">
			<tr>
				<th>Aさん</th>
				<td>
					<counter-hit v-model="countGate1"></counter-hit>
					<hoop-in v-model="inGate1"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate2"></counter-hit>
					<hoop-in v-model="inGate2"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate3"></counter-hit>
					<hoop-in v-model="inGate3"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate4"></counter-hit>
					<hoop-in v-model="inGate4"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGoalA"></counter-hit>
					<hoop-in v-model="inGoalA">ネットイン</hoop-in>
				</td>
				<td>総打数</td>
				<td>加算点</td>
				<td>Total</td>
			</tr>
			<tr>
				<th>Bさん</th>
				<td>
					<counter-hit v-model="countGate5"></counter-hit>
					<hoop-in v-model="inGate5"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate6"></counter-hit>
					<hoop-in v-model="inGate6"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate7"></counter-hit>
					<hoop-in v-model="inGate7"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGate8"></counter-hit>
					<hoop-in v-model="inGate8"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="countGoalB"></counter-hit>
					<hoop-in v-model="inGoalB">ネットイン</hoop-in>
				</td>
				<td>総打数</td>
				<td>加算点</td>
				<td>Total</td>
			</tr>
		</tbody>
	</table>

	<!-- Bootstrap JavaScript -->
	<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script>

	<!-- Main.js -->
	<script src="main.js"></script>
</body>
</html>
main.js
const counterHit = {
	props: ['value'],
	methods: {
		countUp() {
			this.$emit('input', this.value + 1);
		},
		countDown() {
			if ( this.value > 0 ) {
				this.$emit('input', this.value - 1);
			}
		},
	},
	template: `
		<form>
			打数
			<div class="form-group input-group">
				<div class="input-group-prepend">
					<button type="button" @click="countUp" class="btn btn-primary btn-up">+</button>
				</div>
				<input type="number" v-model="value" min="0" disabled class="form-control">
				<div class="input-group-append">
					<button type="button" @click="countDown" class="btn btn-secondary btn-down">-</button>
				</div>
			</div>
		</form>
	`,
};

const hoopIn = {
	props: ['value'],
	methods: {
		add() {
			this.$emit('input', "○");	
		},
		remove() {
			this.$emit('input', "");
		},
	},
	template: `
		<form>
			<slot>フープイン</slot>
			<div class="form-group input-group">
				<div class="input-group-prepend">
					<button type="button" @click="add" class="btn btn-info btn-add">○</button>
				</div>
				<input type="text" v-model="value" disabled class="form-control">
				<div class="input-group-append">
					<button type="button" @click="remove" class="btn btn-secondary btn-remove">☓</button>
				</div>
			</div>
		</form>
	`,
};

new Vue({
	el: '.row-item',
	data() {
		return {
			countGate1: 0,
			countGate2: 0,
			countGate3: 0,
			countGate4: 0,
			countGoalA: 0,
			
			countGate5: 0,
			countGate6: 0,
			countGate7: 0,
			countGate8: 0,
			countGoalB: 0,
			
			inGate1: "",
			inGate2: "",
			inGate3: "",
			inGate4: "",
			inGoalA: "",
			
			inGate5: "",
			inGate6: "",
			inGate7: "",
			inGate8: "",
			inGoalB: "",
		};
	},
	components: {
		'counter-hit': counterHit,
		'hoop-in': hoopIn,
	},
});
style.css
@charset "UTF-8";

body {
	min-width: 1024px;
	font-size: 18px;
	padding: 5px;
}

.col-sm {
	width: 8%;
}

.col-lg {
	width: 14%;
}

.col-md {
	width: 8%;
}

th {
	text-align: center;
}

.form-control {
	padding: 0;
	text-align: center;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

input[type="number"][disabled],
input[type="text"][disabled]{
	background-color: #fff;
}

.btn-up,
.btn-down,
.btn-add,
.btn-remove {
	width: 37px;
}

上記のコードでページを表示すると、このようになります。

まだ2行だけでも、コードは結構長い
まだ2行だけでも、コードは結構長い

まだ2行だけですが、それでも結構長いコードになっています。
今後、行数を3行、4行・・・と増やしていくと、とんでもない長さのコードになってしまいますね。

コードが長いとメンテナンスや修正作業も大変です。
そこで、Vue.jsのv-forを使って短くスッキリとしたコードを書きたいと思います。

スポンサーリンク

Vue.jsのv-forとは

Vue.jsのv-forについては、下記の公式サイトが参考になるかと思います。

リストレンダリング — Vue.js
Vue.js - The Progressive JavaScript Framework

v-forを使うと、配列で指定された複数データを、順番に繰り返してHTMLに表示出来ます。

データを配列で指定する

v-forを使用するためには、データが配列である必要があります。
main.jsを、下記のとおりに書きます。

main.js
<!-- 上記は省略 -->

new Vue({
	el: '.row-item',
	data() {
		return {
			rows: [
				{
					name: 'Aさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Bさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Cさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Dさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Eさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Fさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
			],
		};
	},
	components: {
		'counter-hit': counterHit,
		'hoop-in': hoopIn,
	},
});

上記のように、各行のデータを配列で指定します。
配列の名前は「rows」、各行のデータの名前は「row」になります。
例えば「row.countGate1」で「0」というデータを取得することが出来るようになります。

また、行数を6行に増やして、名前(name)を追加しています。

HTMLにv-forを使う

配列で指定したデータを、v-forを使ってHTMLに順番に表示させます。
index.htmlを、下記のとおりに書きます。

index.html
<!-- 上記は省略 -->

<tbody class="row-item">
	<tr
		v-for="(row, index) in rows"
		:key="index"				
	>
		<th class="col-sm">{{ row.name }}</th>
		<td>
			<counter-hit v-model="row.countGate1"></counter-hit>
			<hoop-in v-model="row.inGate1"></hoop-in>
		</td>
		<td>
			<counter-hit v-model="row.countGate2"></counter-hit>
			<hoop-in v-model="row.inGate2"></hoop-in>
		</td>
		<td>
			<counter-hit v-model="row.countGate3"></counter-hit>
			<hoop-in v-model="row.inGate3"></hoop-in>
		</td>
		<td>
			<counter-hit v-model="row.countGate4"></counter-hit>
			<hoop-in v-model="row.inGate4"></hoop-in>
		</td>
		<td>
			<counter-hit v-model="row.countGoal"></counter-hit>
			<hoop-in v-model="row.inGoal">ネットイン</hoop-in>
		</td>
		<td>総打数</td>
		<td>加算点</td>
		<td>Total</td>
	</tr>
</tbody>

<tbody></tbody>内を、上記のように書き換えています。

v-forを使ってHTMLを繰り返して書くには、繰り返したい要素に下記のコードを書きます。
今回は1行ごとに繰り返して書きたいので、<tr>要素に書いています。

<tr
	v-for="(row, index) in rows"
	:key="index"				
>

v-for=”(データ名) in 配列名” という形で書きます。
これで配列rowsにデータがあれば、そのデータの分だけ<tr>要素内のHTMLが繰り返されます。

indexとは、配列内のデータに自動で割り振られる、データの個数を表す0から始まる数値です。
例えば配列rows内の1行目のデータは、index=0となります。

:keyとは、Vue.jsが要素を識別するための鍵のようなものです。
:keyには一意の値(他と重複しない値)を指定します。
今回はindexを:keyの値としています。

ではページを表示してみます。

v-for使用後のページ
v-for使用後 問題なくページを表示できている

データを追加した6行目まで、問題なくページを表示できています。
カウンター機能も、問題なく動作します。

完成コードを記載

Vue.jsのv-forを使用したコードを、下記に記載します。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>

	<!-- Normalize CSS -->
	<link rel="stylesheet" href="normalize.css">

	<!-- Bootstrap CSS -->
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">

	<!-- Style CSS -->
	<link rel="stylesheet" href="style.css">

	<!-- Vue.js(本番用) -->
	<!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script> -->

	<!-- Vue.js(開発用) -->
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
	<table class="table table-bordered table-striped">
		<thead class="thead-light">
			<tr>
				<th class="col-sm"></th>
				<th class="col-lg">ゲート1</th>
				<th class="col-lg">ゲート2</th>
				<th class="col-lg">ゲート3</th>
				<th class="col-lg">ゲート4</th>
				<th class="col-lg">ゴール</th>
				<th class="col-md">総打数</th>
				<th class="col-md">加算点</th>
				<th class="col-md">Total</th>
			</tr>
		</thead>
		<tbody class="row-item">
			<tr
				v-for="(row, index) in rows"
				:key="index"
			>
				<th class="col-sm">{{ row.name }}</th>
				<td>
					<counter-hit v-model="row.countGate1"></counter-hit>
					<hoop-in v-model="row.inGate1"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="row.countGate2"></counter-hit>
					<hoop-in v-model="row.inGate2"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="row.countGate3"></counter-hit>
					<hoop-in v-model="row.inGate3"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="row.countGate4"></counter-hit>
					<hoop-in v-model="row.inGate4"></hoop-in>
				</td>
				<td>
					<counter-hit v-model="row.countGoal"></counter-hit>
					<hoop-in v-model="row.inGoal">ネットイン</hoop-in>
				</td>
				<td>総打数</td>
				<td>加算点</td>
				<td>Total</td>
			</tr>
		</tbody>
	</table>

	<!-- Bootstrap JavaScript -->
	<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"></script>
	<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"></script>

	<!-- Main.js -->
	<script src="main.js"></script>
</body>
</html>
main.js
const counterHit = {
	props: ['value'],
	methods: {
		countUp() {
			this.$emit('input', this.value + 1);
		},
		countDown() {
			if ( this.value > 0 ) {
				this.$emit('input', this.value - 1);
			}
		},
	},
	template: `
		<form>
			打数
			<div class="form-group input-group">
				<div class="input-group-prepend">
					<button type="button" @click="countUp" class="btn btn-primary btn-up">+</button>
				</div>
				<input type="number" v-model="value" min="0" disabled class="form-control">
				<div class="input-group-append">
					<button type="button" @click="countDown" class="btn btn-secondary btn-down">-</button>
				</div>
			</div>
		</form>
	`,
};

const hoopIn = {
	props: ['value'],
	methods: {
		add() {
			this.$emit('input', "○");	
		},
		remove() {
			this.$emit('input', "");
		},
	},
	template: `
		<form>
			<slot>フープイン</slot>
			<div class="form-group input-group">
				<div class="input-group-prepend">
					<button type="button" @click="add" class="btn btn-info btn-add">○</button>
				</div>
				<input type="text" v-model="value" disabled class="form-control">
				<div class="input-group-append">
					<button type="button" @click="remove" class="btn btn-secondary btn-remove">☓</button>
				</div>
			</div>
		</form>
	`,
};

new Vue({
	el: '.row-item',
	data() {
		return {
			rows: [
				{
					name: 'Aさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Bさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Cさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Dさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Eさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
				{
					name: 'Fさん',
					countGate1: 0,
					countGate2: 0,
					countGate3: 0,
					countGate4: 0,
					countGoal: 0,
					inGate1: "",
					inGate2: "",
					inGate3: "",
					inGate4: "",
					inGoal: "",
				},
			],
		};
	},
	components: {
		'counter-hit': counterHit,
		'hoop-in': hoopIn,
	},
});

style.cssに変更はありません。

おわりに

長くなったHTMLコードを、Vue.jsのv-forを使用して、短くスッキリと書く方法について解説してみました。

ただし、HTMLは短くなりましたが、dataを配列で指定しているJavascriptのコードが長くなってしまいました。
この長くなったJavascriptのコードを短くする方法は、別の記事で解説してみたいと思います。

今回の記事が皆さんの参考になれば幸いです。
以上、ちゃんカマでした。

コメント

タイトルとURLをコピーしました