Happy Holiday
これはROXXアドベントカレンダー25日目の投稿です。(12時過ぎましたがクリスマス気分のうちはまだクリスマス!)
業務ではサーバー (Laravel製) に対して、 フロントエンド (Nuxt.js製) からリクエストを送る構成になっています。
データ内の検索機能などは基本的にはサーバー側で処理することが多いのですが、最近フロントエンドで同機能を実装する機会がありました
フロントエンドだけで処理を行うため、処理速度は速くなり、 API へのアクセスも削減することができます🚀
この記事ではその実装方法について書きます。
実装ページ (CodePen)
実装概要
- Vue.js
- 使用するAPI: PokéAPI
- ポケモン情報が取れる API
- ページ作成時に API リクエストを行う
- 検索をフロントエンドで処理する
検索機能詳細
- テキストに含まれる文字列で検索
- 検索文字列をスペースで分けることで、 AND 検索 (もしくは OR 検索) が可能
コード説明
Template
template はこんな感じで書きました。
取得したデータのうち、モンスターの名前だけをリストにします。
<div id="app">
<input
v-model="searchText"
type="text"
placeholder="Enter text"
>
<div
v-for="(monster, key) in monstersList"
:key="key"
>
<p>{{ monster.name }}</p>
</div>
</div>
Script - データ取得
axios で API からデータを取得します。
data 内の monsters
に取得したデータを代入します。オブジェクトで返ってくるので Object.freeze()
で凍結させてもいいかもしれません。
searchText
には検索キーワードが入ります。実際の処理はこのあと書きます。
new Vue({
el: '#app',
data:() => ({
monsters: null,
searchText: '',
url: 'https://pokeapi.co/api/v2/berry?limit=200'
}),
async created() {
const res = await axios.get(this.url)
this.monsters = res.data.results
}
});
Script - 検索キーワードの処理
このあとモンスター名と照合させるために、検索キーワード内の文字列を先に処理しましょう。
例えば leppa
を検索する際、 le ppa
というように文字列にスペースが入ったときに、 AND 検索か OR 検索を行えるのは自然かと思います。
そのための方法として、ここでは 1 文字ずつ配列の要素に入れる実装にします。
computed: {
splittedSearchText() {
return this.searchText.split(/[\s]+/)
}
}
こうすることで先ほどの le ppa
という文字列を ["le", "ppa"]
という配列に置き換えます。
Script - 検索実行
検索機能とは、入力された文字列と同じ文字列を含むモンスター名を抽出することです。
まずは 入力された文字列と同じ文字列を含む
という処理を書きます。
monsters の配列に対して、条件を満たすかどうかというメソッドを加えます。
AND 検索の場合は Array.every を、 OR 検索の場合は Array.some を使うことでこれを実現します。
サンプルコードでは AND 検索を実装します。
モンスター名の文字列中に、検索キーワード (splittedSearchText) が含まれるかを見ていくので、モンスターを 1 つずつ関数に渡し、照合して返すメソッドを書きます。
methods: {
filteredMonsters(monster) {
if (this.splittedSearchText.every(el =>
monster.name.includes(el)
)
) {
return true
}
}
}
上記のメソッドを呼び出しましょう。
computed に記述し、テキストが入力されたと同時に処理が走るようにします。
// computed に追加
monstersList() {
if (!this.monsters) { // モンスターが存在しないときは空配列を返す
return []
}
if (!this.searchText.length) { // 検索文字列が存在しないときは monsters をそのまま返す
return this.monsters
}
return this.monsters.filter(monster => {
return this.filteredMonsters(monster)
})
}
完成 + おまけ
いささか簡単なコードですが、フロントエンドで検索機能を実装することができました 🎉
喜びたいところなんですが、同じ検索機能を日本語の文字列で行う際には少し注意が必要です。アルファベットを入力中にも検索が走ってしまうので、リストのモンスターが増えたり減ったりとても見づらい挙動を起こします。
そんなときは、 compositionstart と compositionend を使って検索するタイミングを調整しましょう。
<div id="app">
<input
v-model="searchText"
type="text"
placeholder="Enter text"
@compositionstart="isComposing = true"
@compositionend="isComposing = false"
>
<div
v-for="(monster, key) in monstersList"
:key="key"
>
<p>{{ monster.name }}</p>
</div>
</div>
data: () => ({
isComposing: false
}),
// computed に追加
monstersList() {
if (!this.monsters) {
return []
}
if (!this.searchText.length) {
return this.monsters
}
/* 追加コード */
if (this.isComposing) { // isComposing が true (日本語入力変換中) のとき、 monsters を返す
return this.monsters
}
return this.monsters.filter(monster => {
return this.filteredMonsters(monster)
})
}
このように記述することで、日本語を変換しているときには isComposing
が true となるので、処理を走らないよう制御することができました!
フロントエンドでできることがいっぱいあって嬉しいですね。
Happy Programming! 🌟 Happy Holiday! ☃️