💨

Vue.js で検索機能

2022/06/18に公開

実務で実装した内容の振り返りです。
※ 紹介している値は実務とは無関係なものに変更しています。

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 を使用しました。
namesearchKeyword の検索結果によって真偽値を返す関数を用意して、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