Vue.js で検索機能
実務で実装した内容の振り返りです。
※ 紹介している値は実務とは無関係なものに変更しています。
vue: "^2.6.12"
要件
API で取得したデータをもとにフロントでデータの状態によって配列を作り直しているデータがあります。その値から input
などから入力された値で文字列検索をできるようにしたい。
文章だけでは分かりづらいため、値を見ながら説明します。
①APIリクエストして取得されるデータ。
[
{
"id": 1,
"name": "佐藤",
"age": 20,
"register_date": "2022-01-01"
},
{
"id": 2,
"name": "田中",
"age": 22,
"register_date": "2022-01-02"
},
{
"id": 3,
"name": "加藤",
"age": 30,
"register_date": "2022-02-01"
},
{
"id": 4,
"name": "山田",
"age": 25,
"register_date": "2022-02-03"
}
]
② ①の情報をもとにフロントで状態によってグループ分けしている。
usersGroups = [
// 2022年1月に登録したグループ
[
{
id: 1,
name: "佐藤",
age: 20,
register_date: "2022-01-01",
},
{
id: 2,
name: "田中",
age: 22,
register_date: "2022-01-02",
},
],
// 2022年2月に登録したグループ
[
{
id: 3,
name: "加藤",
age: 30,
register_date: "2022-02-01",
},
{
id: 4,
name: "山田",
age: 25,
register_date: "2022-02-03",
},
],
],
上記のデータを使って、入力した内容にヒットしたユーザをブラウザに表示させたい
実装
フィルタリング用のデータを用意
usersGroups
を固定値と考え、別でフィルタリング用のデータを用意します。
data() {
return {
usersGroups: [],
filteredUsersGroups: [] // 追加
}
}
入力した内容を usersGroups
で検索し、結果をフィルタリング用の filteredUsersGroups
に注入することで入力した結果をフロントに表示するような方針で実装します。
フィルタリング方法
検索キーワード用の値を用意
data() {
return {
usersGroups: [],
filteredUsersGroups: [],
searchKeyword: "" // 追加
}
}
検索キーワードに入力された値と合致する user.name
を探し出します。
検索にはJavaScriptの indexOf を使用しました。
引数に渡した値にヒットすればヒットした文字数の index が取れ、ヒットしなかった場合 -1
が返ってくるので、こちらで検索を実施します。
user.name.indexOf(this.searchKeyword) !== -1
検索した結果を再度配列にして filteredUsersGroups
に注入したかったので、検索には filter を使用しました。
name
と searchKeyword
の検索結果によって真偽値を返す関数を用意して、filter の callback 関数として使用します。
filterByName(user) {
// キーワードが未入力のときは全て true とする
if (!this.searchKeyword) return true
if (user.name.indexOf(this.searchKeyword) !== -1) {
return true
} else {
return false
}
}
検索結果によって新しく生成された配列を現在の filteredUsersGroups
と置き換えたかったので、 splice を使用しました。
全体
filterByName(user) {
// キーワードが未入力のときは全て true とする
if (!this.searchKeyword) return true
if (user.name.indexOf(this.searchKeyword) !== -1) {
return true
} else {
return false
}
}
searchUsers() {
for (
let groupIndex = 0;
groupIndex < this.usersGroups.length;
groupIndex++
) {
let result = this.usersGroups[groupIndex].filter(
this.filterByName
)
this.filteredUsersGroups.splice(groupIndex, 1, result)
}
},
詰まった点
this.filteredUsersGroups.splice(groupIndex, 1, result)
上記時点で、filteredUsersGroups
の値は置き換わるのですが、この時点で usersGroups
の値も変わってしまっていました。こちらの原因を掴むのに少し時間がかかりました。
原因として usersGroups
の値を単純に filteredUsersGroups
にコピーしてしまっていたため「浅いコピー」となっていたことでした。
this.filteredUsersGroups = this.usersGroups
以下に修正して「深いコピー」とすることで解消しました。
this.filteredUsersGroups = JSON.parse(
JSON.stringify(this.usersGroups)
) ?? [[]]
「浅いコピー」「深いコピー」については別記事としてまとめようと思います。
Discussion