コピペでOK!Vue.jsでリアルタイム検索をつくる方法

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、この頃Laravel情報ばかりを記事にしているということで前回記事ではVue.jsの話題をお届けしました。ということで今回もその流れに乗ってVue.jsで知っておくととても便利なテクニックをご紹介したいと思います。

内容としては、「リアルタイム(オフライン)検索」です。

例えば以下のようなウェブページを想像してみてください。

  • ある施設をリスト表示する
  • リスト上部にある検索ボックスで検索ができる
  • その検索ボックスに入力した時点で該当するデータだけ表示する

つまり、検索はJavaScript側でするので毎回Ajax通信する必要がありません(オフラインで完結)。だから、とても高速に検索ができるというわけですね。

※ただし、ページが読み込まれる時点で全てのデータを用意する必要があるのであまりにもデータが多い場合はリアルタイム検索は向いてるとは言えません。その場合は通常通り1ページずつデータをAjaxで取得する方がいいでしょう。

では、Vue.jsを使って、この「リアルタイム検索」を実装してみましょう。

【追記:2020.12.02】
訪問ユーザーさんからご質問をいただいたので、画像を表示するサンプルも「おまけ」として追記しました。また、Vueのバージョンを2.6.12にアップグレードしました。
【追記:2021.01.03】
訪問ユーザーさんからご質問をいただいたので、v-for:keyを追加しました。

開発環境: Vue 2.6.12

Vue.jsでリスト表示する

まずはVue.jsを使って5件のテストデータをリスト表示してみましょう。

<html>
<body>
<div id="app">
    <table>
        <tr v-for="user in users" :key="user.id">
            <td v-text="user.id"></td>
            <td v-text="user.name"></td>
            <td v-text="user.email"></td>
        </tr>
    </table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"></script>
<script>

    new Vue({
        el: '#app',
        data: {
            users: [
                {
                    id: 1,
                    name: '鈴木太郎',
                    email: 'suzukitaro@example.com'
                },
                {
                    id: 2,
                    name: '佐藤二郎',
                    email: 'satoujiro@example.com'
                },
                {
                    id: 3,
                    name: '田中三郎',
                    email: 'tanakasaburo@example.com'
                },
                {
                    id: 4,
                    name: '山本四郎',
                    email: 'yamamotoshiro@example.com'
                },
                {
                    id: 5,
                    name: '高橋五郎',
                    email: 'takahashigoro@example.com'
                },
            ]
        }
    });

</script>
</body>
</html>

内容としては、データをVueのdataに格納して、それをv-forで一つずつ表示するだけのシンプルなものです。

実行結果はこうなります。

v-modelでバインディングした検索ボックスをつくる

次にリアルタイム検索をするための検索ボックスを追加します。

まず、dataに検索キーワード用の変数「keyword」を追加。

data: {
    keyword: '',
    users: [
        {
            id: 1,
            name: '鈴木太郎',
            email: 'suzukitaro@example.com'
        },
        {
            id: 2,
            name: '佐藤二郎',
            email: 'satoujiro@example.com'
        },
        {
            id: 3,
            name: '田中三郎',
            email: 'tanakasaburo@example.com'
        },
        {
            id: 4,
            name: '山本四郎',
            email: 'yamamotoshiro@example.com'
        },
        {
            id: 5,
            name: '高橋五郎',
            email: 'takahashigoro@example.com'
        },
    ]
}

そして、HTMLにv-modelが入ったinputタグを記述します。

<div id="app">
    <input type="text" v-model="keyword">
    <table>
        <tr v-for="user in users" :key="user.id">
            <td v-text="user.id"></td>
            <td v-text="user.name"></td>
            <td v-text="user.email"></td>
        </tr>
    </table>
</div>

これを実行すると以下のようになります。

リアルタイム検索のロジックを追加する

では、ここからが本題のリアルタイム検索です。
検索ボックスに文字が入力された瞬間、関連データだけ表示する方法です。

まずリアルタイムにかかわらずデータ検索するには、データのフィルター機能が必要です。

例えば、検索ワードが「鈴木」でデータが以下のものだった場合、ひとつずつデータをチェックして「鈴木」という文字列が含まれているかどうかをチェックし、もし含まれているのならそのデータを残して他はスルーしなければいけません。

users: [
    {
        id: 1,
        name: '鈴木太郎',
        email: 'suzukitaro@example.com'
    },
    {
        id: 2,
        name: '佐藤二郎',
        email: 'satoujiro@example.com'
    },
    {
        id: 3,
        name: '田中三郎',
        email: 'tanakasaburo@example.com'
    },
    {
        id: 4,
        name: '山本四郎',
        email: 'yamamotoshiro@example.com'
    },
    {
        id: 5,
        name: '高橋五郎',
        email: 'takahashigoro@example.com'
    },
]

これを実装するためには、Vueのcomputedを利用するといいでしょう。
computedはデータが変更(つまり、ここでは検索キーワードが入力)されると同時にコンテンツ内容も変更することができますし、さらにキャッシュが効くので次回からのレンダリングも高速表示できるようになります。

※詳しくは、Vueの「methods」と「computed」の違いをご覧ください。

例えばこんな形です。

computed: {
    filteredUsers: function() {

        // ここにデータをフィルターするロジック

    }
}

そして、computedで作成したキー(ここではfilteredUsers)は以下のように変数の代わりとして利用することができます。

<table>
    <tr v-for="user in filteredUsers" :key="user.id">
        <td v-text="user.id"></td>
        <td v-text="user.name"></td>
        <td v-text="user.email"></td>
    </tr>
</table>

これで、あとはデータをフィルターするロジックを記述してやれば、リアルタイム検索は完成です。

【追記:2021.01.03】訪問ユーザーさんからESLintにエラーが出ているというご指摘をいただきましたので、:key="****"を追加しました。公式ドキュメントにも「できるだけつけた方がいいよ」と書かれています。皆さん、いつもありがとうございます😊✨なお、:keyについては以下の記事をご覧ください。

📝 Vue.js の v-for で気をつけておくべきこと

実際のコードはこうなります。

filteredUsers: function() {

    var users = [];

    for(var i in this.users) {

        var user = this.users[i];

        if(user.name.indexOf(this.keyword) !== -1) {

            users.push(user);

        }

    }

    return users;

}

例のIEも考慮にいれているのでES6ではなくてすみません。(IEさん、もうね・・・・^^;)

重要なのはif文で各データに検索キーワードが含まれているかチェックしている部分です。もし含まれていれば、配列usersにそのデータを追加するようになっています。

さらに、もしnameだけじゃなくemailでもフィルターをかけたい場合は以下のようにするといいでしょう。

if(user.name.indexOf(this.keyword) !== -1 ||
    user.email.indexOf(this.keyword) !== -1) {

    users.push(user);

}

はい!
これだけでVueのリアルタイム検索は完成です。昔と比べると格段に効率が上がってますね。

実際の全コード

以下が今回テスト用に作ったリアルタイム検索の全コードです。
Vue.jsもcdnを読み込んでいるので、このままコピペで動くと思います。

<html>
<body>
<div id="app">
    <input type="text" v-model="keyword">
    <table>
        <tr v-for="user in filteredUsers" :key="user.id">
            <td v-text="user.id"></td>
            <td v-text="user.name"></td>
            <td v-text="user.email"></td>
        </tr>
    </table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"></script>
<script>

    new Vue({
        el: '#app',
        data: {
            keyword: '',
            users: [
                {
                    id: 1,
                    name: '鈴木太郎',
                    email: 'suzukitaro@example.com'
                },
                {
                    id: 2,
                    name: '佐藤二郎',
                    email: 'satoujiro@example.com'
                },
                {
                    id: 3,
                    name: '田中三郎',
                    email: 'tanakasaburo@example.com'
                },
                {
                    id: 4,
                    name: '山本四郎',
                    email: 'yamamotoshiro@example.com'
                },
                {
                    id: 5,
                    name: '高橋五郎',
                    email: 'takahashigoro@example.com'
                },
            ]
        },
        computed: {
            filteredUsers: function() {

                var users = [];

                for(var i in this.users) {

                    var user = this.users[i];

                    if(user.name.indexOf(this.keyword) !== -1 ||
                        user.email.indexOf(this.keyword) !== -1) {

                        users.push(user);

                    }

                }

                return users;

            }
        }
    });

</script>
</body>
</html>

実行結果(サンプル)

今回の実行結果はこちらです。

おまけ:検索結果に画像を表示する

【追記:2020.12.02】
訪問ユーザーさんからご質問をいただいたので、検索結果に画像を表示するサンプルもご紹介します。貴重なご意見ありがとうございました😊

検索結果に画像を含めるには、まず以下のように検索データに画像のURLを追加します。

new Vue({
    el: '#app',
    data: {
        keyword: '',
        users: [
            {
                id: 1,
                name: '鈴木太郎',
                email: 'suzukitaro@example.com',
                imageUrl: 'https://via.placeholder.com/300x200?text=Image-1' // 👈 画像のURL
            },

            // 以下省略

あとは、HTML側のループ部分に<img>タグをつくり、srcURLを入れてあげればOKです。

<table>
    <tr v-for="user in filteredUsers" :key="user.id">
        <td v-text="user.id"></td>
        <td v-text="user.name"></td>
        <td v-text="user.email"></td>
        <td>
            <img :src="user.imageUrl">
        </td>
    </tr>
</table>

このやりかたを応用すると画像だけでなく、<video><audio>タグなんかでも使えますよ👍

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで今回はVue.jsを使ってリアルタイム検索を作る方法をお届けしました。

ここまで短いコードでこういうページを作れるってやっぱりバインディングは強力ですね!

ぜひみなさんも開発に役立ててくださいね。

ではでは〜!

このエントリーをはてなブックマークに追加       follow us in feedly