【Ajax】Railsでインクリメンタルサーチの実装

インクリメンタルサーチというやつを実装したよ。



ChatSpaceでは、作ったグループ内に集まったメンバー間でのメッセージのやり取りをするという仕様なわけだけれども。

今回追加したインクリメンタルサーチは、グループ新規作成時とグループ情報編集時に、ユーザーネームを検索するという機能のために実装したよ。

コードの解説に行く前に。

<h2> インクリメンタルサーチとは </h2> 
検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる。 逐語検索、逐次検索とも。
(引用 : https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%82%BF%E3%83%AB%E3%82%B5%E3%83%BC%E3%83%81)

グーグル検索で「あ」とか打ち込むと、検索候補に「アマゾン」ってできてきたりするじゃないですか。
あれのことです。
今回はイメージしやすくていいですね。
では解説に行きます。

<h2> インクリメンタルサーチの実装 </h2> 


1.ルーティングなどのAPIの準備をする
今回はusers#indexを使うためのルーティングを用意します。

/config/routes.rb

```ruby
Rails.application.routes.draw do
  (省略)
  resources :users  , only:[:index,:edit,:update,:show]
  (省略)
  # ただindexアクションを追加しただけです。  
``` 


<h3>2.テキストフィールドを作成する</h3>
これはファイルによりけりなので、ソースコードを省略します。　
ちなみに僕の場合の作業ファイルは下記でした。
app/views/groups/_form.html.haml
(同階層のindex.html.hamlからrenderでformを読みだしてます。)


<h3>3.テキストフィールドに入力されるたびにイベントが発火するようにする</h3>

app/assets/javascripts/user.js

```javascript
    $('#user-search-field').on('keyup', function(e){
        var input = $("#user-search-field").val();
``` 
'#user-search-field'がユーザー検索フォームです。
ここに文字を打ち込むたびに処理が発火します。
2行目は、変数input対して、フォームに入力された値を代入しています。

<h3>4.イベント時に非同期通信できるようにする</h3>

```javascript 

    $('#user-search-field').on('keyup', function(e){
        var input = $("#user-search-field").val();

        $.ajax({
            type: 'GET',                // HTTPメソッドはGETで
            url:  '/users',             // /usersのURLに (これによりusersコントローラのindexアクションが起動)
            data: { keyword: input},    // keyword: inputを送信する
            dataType: 'json'            // サーバから値を返す際はjsonである
        })

        .done(function(users){                // usersにjson形式のuser変数が代入される。複数形なので配列型で入ってくる
            
            if (input.length === 0) {         // フォームの文字列長さが0であれば、インクリメンタルサーチ結果を表示しないようにする
                $('#user-search-result').empty();
              }

            else if (input.length !== 0){     // 値が等しくないもしくは型が等しくなければtrueを返す。
                $('#user-search-result').empty();
                users.forEach(function(user){ // users情報をひとつずつとりだしてuserに代入
                    appendUser(user)
                });
            }

            else {
                $('#user-search-result').empty(); // ユーザーが見つからなければ「見つからない」を返す。
                appendErrMsgToHTML("一致するユーザーが見つかりません");
            }
        })

        .fail(function() {
            alert('ユーザー検索に失敗しました');
        });
    });
```
ちょっと長いので詳細はコメントアウトの記述に譲りますが、流れを解説します。

(※appendUser(user)とappendErrMsgToHTML("一致するユーザーが見つかりません")はメソッドです。後述します。)

keyupにより発火。

ajaxで通信をする。その際のメソッド、リクエストの指定、データの指定、データタイプの指定をしています。

これをコントローラが受け取って、jbuilderの記述に従ってjson形式でJavaScriptに戻ってきます。
(コントローラとjbuilderの記述は冗長になるので省きます。)

done⇨通信が成功した場合は文字列の長さと型(integerかstringか)で判断します。

fail   ⇨アラートを出します。

<h3>5.非同期通信の値を元に、HTMLに反映します。</h3>

```javascript
$(document).on('turbolinks:load', function(){

    var search_list = $("#user-search-result");
    var member_list = $("#member-append");

    function appendUser(user){
        var html = 
                    `<div class="chat-group-user clearfix">
                        <p class="chat-group-user__name">${user.name}</p>
                        <div class="user-search-add chat-group-user__btn chat-group-user__btn--add" data-user-id=${user.id} data-user-name=${user.name}>追加</div>
                    </div>`;
                    search_list.append(html);
    }

    function appendErrMsgToHTML(msg){
        var html = 
                    `<div class="chat-group-user clearfix">
                        <p class="chat-group-user__name">${msg}</p>
                    </div>`;
                    search_list.append(html);
    }
```


4の際に記述されていた、appendUser(user)とappendErrMsgToHTML("一致するユーザーが見つかりません")のメソッドを定義しています。
()内に記載しているのは引数です。


これでインクリメンタルサーチの大まかな流れは終わりです。
とっかかりは凄くハードルが高く感じますが、一個一個解説していくとそんなに難しくないかなぁと思います。

ポイントはやっぱり$.ajax内部で指定している部分かなと思います。
自動更新機能の実装なんかもしたのですが、ここの設定によるミスで頻繁にエラーが発生しました。。。

明日は自動更新機能も書きたいと思っています。