今回、はじめてvueを触ってみました。
まだまだ深くまで知れていないのですが、備忘録として残そうと思います。
Vueのversion3を使用しています。
完成コード
今回、vue3で記述しました。Vue2との違いがあまりわかっていないのですが、最初のアプリの作成方法が異なると思います。
また、CDNを読み込んで作成しました。
Jsfiddleで見たほうが見やすいです。
JSONデータを読み込む
今回、axiosというライブラリを使用して、jsonを読み込んでいます。
詳しい内容はわかっていないのですが、サーバーからデータを取得したりするときに便利なライブラリのようです。
JSfiddleでは、records内からデータをそのまま読み込ませていますが、下記の記述でjsonからのデータを参照するようになります。
mounted() {
//自分の環境にあわせたパスを入力してください。
var json = './mountaion.json';
axios.get(json).then(x => {
this.records = x.data
}).catch((error) => alert('正しくjsonデータが読み込まれていません。error:' + error));
},
JSONファイルからデータを読み込みたい場合は、データのrecordsの値を空にしてください。
records: [],
検索項目をv-forを使って、ループさせる
検索項目内の、地域と都道府県という値は、v-forを使用して、ループさせています。
area、prefectureというのは、dataでそれぞれ定義をしています。
selectの方は、ループを2回、回しています。最初は、prefectureの配列で回し、optionの値は、prefectureのnameの配列でループを回しています。
<tr>
<th>地域</th>
<td>
<label v-for="(area, index) in area" :key="area" style="margin-right: 15px;">
<input v-model="selectedArea" name="keyword" type="checkbox" :value="area.label">
{{area.label}}
</label>
</td>
</tr>
<tr>
<th>都道府県</th>
<td>
<select v-model="selectedPrefecture" style="width: 100%;">
<option value="" selected>都道府県で絞りこむ</option>
<template v-for="(prefecture, index) in prefecture" :key="prefecture">
<optgroup :label="prefecture.area">
<template v-for="(name, index) in prefecture.name" :key="name">
<option :value="name">{{name}}</option>
</template>
</optgroup>
</template>
</select>
</td>
</tr>
keyの値は必ず設定すること!また、このkeyの値は一意でなければいけません。
v-forの詳しい記述方法は、下記参考ください!
v-modelでinputをバインドして、値を取得する
次に、v-modelを使用して、検索項目のそれぞれの値をバインドさせています。
こうすることにより、後に検索で絞り込みをさせるための、地域のチェックした値、都道府県の選択した値、キーワードの入力した値が簡単に取得できるようになります。
<tr>
<th>地域</th>
<td>
<label v-for="(area, index) in area" :key="area" style="margin-right: 15px;">
<input v-model="selectedArea" name="keyword" type="checkbox" :value="area.label">
{{area.label}}
</label>
</td>
</tr>
<tr>
<th>都道府県</th>
<td>
<select v-model="selectedPrefecture" style="width: 100%;">
<option value="" selected>都道府県で絞りこむ</option>
<template v-for="(prefecture, index) in prefecture" :key="prefecture">
<optgroup :label="prefecture.area">
<template v-for="(name, index) in prefecture.name" :key="name">
<option :value="name">{{name}}</option>
</template>
</optgroup>
</template>
</select>
</td>
</tr>
<tr>
<th valign="middle">キーワード</th>
<td>
<input name="keyword_search" maxlength="80" type="text" v-model="searchKeywords">
</td>
</tr>
そして、それぞれの値をdataで配列で入るように定義しておきます。
selectedArea: [],
selectedPrefecture: [],
searchKeywords: [],
検索内容のデータをfilterにかける
filterItems()で選択した検索項目に応じて、データをfilterさせる
見出しのとおりなのですが、そちらの処理は、下記になります。
filterItems() {
let filtered = this.records;
if (this.selectedArea.length > 0) {
// filterに該当のものを、配列でreturn
filtered = filtered.filter((item) => {
// チェックボックスにチェックされているかを、分岐
for (let i = 0; i < this.selectedArea.length; i++) {
// this.selectedAreaはオブジェクトなので、[i]をつけることで、配列の項目を一つずつ取り出して、参照している
if (item.area.includes(this.selectedArea[i])) {
// includesで一致する場合は true を返して、フィルタリングする
return true;
}
}
return false;
});
}
if (this.selectedPrefecture.length > 0) {
filtered = filtered.filter((item) => {
if (item.prefecture.includes(this.selectedPrefecture)) {
return true;
}
return false;
});
}
// キーワード検索
if (this.searchKeywords.length > 0) {
const searchKeywords = this.searchKeywords.split(/\s/);
filtered = filtered.filter(function (item) {
for (let i = 0; i < searchKeywords.length; i++) {
console.log(searchKeywords[i]);
// 山の名前とかなにヒットしない場合は、falseを返す
if ((!item.name.includes(searchKeywords[i])) && (!item.kana.includes(searchKeywords[i]))) {
return false
break;
}
}
return true
});
}
return filtered;
}
},
長いので、それぞれ区切って説明したいと思います。
まず、山の情報のデータを変数に格納させます。こちらをletにさせているのは、filterをかけて、変数を上書きさせる必要があるので、constではなく、letになります。これだけの記述にすると、recordsで呼び出した値がずらっと表示されるようになります。
filterItems() {
let filtered = this.records;
//この間に条件をかいて、filteredの値をfilterさせる。
return filtered;
}
selectedAreaの選択内容に応じて、絞り込みをさせる
まず最初のif文で、地域のチェックボックスが、選択されたときを条件にしています。
そして、filterメソッドを使い、地域のチェックしたvalueとデータのareaの値がマッチしたものが、filteredに返るようにしています。
if (this.selectedArea.length > 0) {
// filterに該当のものを、配列でreturn
filtered = filtered.filter((item) => {
// チェックボックスにチェックされているかを、分岐
for (let i = 0; i < this.selectedArea.length; i++) {
// this.selectedAreaはオブジェクトなので、[i]をつけることで、配列の項目を一つずつ取り出して、参照している
if (item.area.includes(this.selectedArea[i])) {
// includesで一致する場合は true を返して、フィルタリングする
return true;
}
}
return false;
});
}
注意点としては、vueのdataを参照する際に、thisをつけることです!
また、this.selectedAreaがオブジェクトのため、[i]をつけることで、配列の要素を一つずつ参照するようにしています。
selectedPrefectureの選択内容に応じて、絞り込みをさせる
こちらは、都道府県の選択した内容とマッチさせています。
先ほど違って、this.selectedPrefectureのvalueの値がそのまま返ってきているので、ループをさせていません。
この理由がいまいちわかっていません。。
if (this.selectedPrefecture.length > 0) {
filtered = filtered.filter((item) => {
if (item.prefecture.includes(this.selectedPrefecture)) {
return true;
}
return false;
});
}
キーワード入力した値に応じて、絞り込みをさせる
最後に、キーワードで入力した値に応じて、絞り込みをさせています。対象は、山の名前と山のふりがなにしています。
if (this.searchKeywords.length > 0) {
const searchKeywords = this.searchKeywords.split(/\s/);
filtered = filtered.filter((item) => {
for (let i = 0; i < searchKeywords.length; i++) {
// 山の名前とかなにヒットしない場合は、falseを返す
if ((!item.name.includes(searchKeywords[i])) && (!item.kana.includes(searchKeywords[i]))) {
return false
break;
}
}
return true
});
}
下記で、入力した値を空白文字ごとに、分割させています。
const searchKeywords = this.searchKeywords.split(/\s/);
下記で、キーワード検索に入力値があった場合、山の名前とかなにマッチする値がなければfalseを返す。
つまり、一致する場合は、trueを返します。
for (let i = 0; i < searchKeywords.length; i++) {
// 山の名前とかなにヒットしない場合は、falseを返す
if ((!item.name.includes(searchKeywords[i])) && (!item.kana.includes(searchKeywords[i]))) {
return false
break;
}
}
filterしたデータを表示する
下記の記述で、filterしたデータを表示させています。
filterItemsというのが、computedプロパティで検索項目に応じて、filterをした配列が返ってきます。
このデータをv-forディレクティブを使って、ループを回し、検索結果を表示していることになります。
そして、ヒットした件数は、{{filterItems.length}}で配列のカウントをすることで、表示させています。
<div>ヒットした数:{{filterItems.length}}</div>
<ul class="checkedArea">
<li v-for="(item, index) in filterItems" :key="item.id">
<h4>名前:{{item.name}} ({{item.kana}})</h4>
<div>エリア:{{item.area}}</div>
<div>都道府県:{{item.prefecture}}</div>
</li>
</ul>
computedプロパティについては、下記のサイトがわかりやすいです。
まとめ
検索機能なんてできるのか、正直とても不安だったのですが、vueだとこんなに記述が簡単で実装できるのかと驚きです!
なにより、さわりのvueの知識でも、ある程度作成することができます。
今後は、Reactにも手を出していこうと思ってます。vueがバージョンがあがり、reactの構成に寄せてきているなんて話も聞いたので。。