Railsでajaxを用いた簡単なインクリメンタルサーチを実装する


## そもそもインクリメンタルサーチとは
検索窓に文字を入力すると、入力ごとに候補を表示させる検索方法です。
Googleで何か文字を打つと、検索窓の下に、ピョコンと検索候補が出てきますよね。
あれです。

## 対象読者
インクリメンタルサーチに恐怖を覚えている人。
rails、jqueryの基礎はある程度理解している人。

## 準備
まず、サンプルとなるアプリケーションを作っていきましょう。
今回は、scaffoldを使用して進めていきます。
ユーザーの名前と年齢を登録できるアプリケーションを作成してみます。
ターミナルで、以下のコマンドを打ち込みます。

```terminal:ターミナル
rails new scaffold_sample
rails g scaffold user name:string age:integer

```


すると、MVCで必要なファイルを一気に作成し、ルーティングまで設定してくれます。さらにbundle installまで行ってくれます。
マイグレーションファイルを確認して、データベースを作成しましょう。

```ruby:db/migrate/0000000000_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :age

      t.timestamps
    end
  end
end
```

```terminal:ターミナル
rake db:migrate
```

これでデータベースも作成され、準備が整いました。
サーバーを立ち上げ、http://localhost:3000/users に接続し、正しく表示がされているか確認してみましょう。

```terminal:ターミナル
rails s
```

![スクリーンショット 2017-05-17 19.52.48.png](https://qiita-image-store.s3.amazonaws.com/0/177591/82e7d39e-bcf4-431e-472f-ba95c11c9822.png "スクリーンショット 2017-05-17 19.52.48.png")

画像のように表示されていれば成功です。
では、試しにユーザー登録をしてみます。
ユーザー情報がデータベースにしっかりと登録ができていれば、下の画像のように表示されます。

![スクリーンショット 2017-05-17 19.54.23.png](https://qiita-image-store.s3.amazonaws.com/0/177591/0c86898a-ff47-10bb-2268-a925b76e4641.png "スクリーンショット 2017-05-17 19.54.23.png")

## インクリメンタルサーチを実装する
さて、ここからいよいよインクリメンタルサーチを実装していきます。
検索語句を入力したら、その度にユーザー名が表示されるようなものを作っていきます。

### ビューを編集する
まずは、検索窓をビューに作りましょう。
inputタグを使い、フォームを作ります。

```html:app/views/index.html.erb
<h1>Users</h1>
<!-- ここから追記 -->
<input type=form id="form" placeholder="ユーザー名を入力してください" style="width: 250px;"/>
```

jsファイルでは、id（もしくはクラス）を指定して実装を進めるので、必ずidを付与しておきます。

### jsファイルで、検索語句を取得する
さて、これだけでは何も意味を持たないフォームができただけですね。
このフォームの情報をjsファイルで取得できるようにしましょう。
app/assets/javascriptに、user.js（名前は任意）というファイルを作成します。
その中に、以下のような記述を加えます。

```javascript:app/assets/javascript/user.js
$(document).on('turbolinks:load', function(){ //リロードしなくてもjsが動くようにする
  $(document).on('keyup', '#form', function(e){ //このアプリケーション(document)の、formというid('#form')で、キーボードが押され指が離れた瞬間(.on('keyup'...))、eという引数を取って以下のことをしなさい(function(e))
    e.preventDefault(); //キャンセル可能なイベントをキャンセル
    var input = $.trim($(this).val()); //この要素に入力された語句を取得し($(this).val())、前後の不要な空白を取り除いた($.trim(...);)上でinputという変数に(var input =)代入
  });
});
```
これでユーザーが入力した検索語句を取得することができました。

### ajaxを用いてデータを渡す
このデータを、ajax通信を用いて、json形式でコントローラーに渡します。
下記のように追記しましょう。

```javascript:app/assets/javascript/user.js
$(document).on('turbolinks:load', function(){
    $(document).on('keyup', '#form', function(e){
    e.preventDefault();
    var input = $.trim($(this).val());
    //ここから追記
    $.ajax({ //ajax通信で以下のことを行います
      url: '/users/search', //urlを指定
      type: 'GET', //メソッドを指定
      data: ('keyword=' + input), //コントローラーに渡すデータを'keyword=input(入力された文字のことですね)'にするように指定
      processData: false, //おまじない
      contentType: false, //おまじない
      dataType: 'json' //データ形式を指定
    })
  });
});
```

これで、ajax通信でデータをコントローラーに渡すことができました。

### コントローラーを編集する
さて、ajaxにてURLは'/users/search'に、メソッドは'GET'を指定しました。
これの通り、ルーティングとコントローラーを設定します。

```ruby:config/routes.rb
Rails.application.routes.draw do
  resources :users do
    collection do
      get 'search'
    end
  end
end
```

```ruby:app/controllers/users_controller.rb
def search
end
# privateの前に追記
```

このsearchアクションの中でデータを受け取り、検索をかけたいですね。
また、新たにsearchというビューをつくるのではなく、検索した名前はindexに表示させたいので、そのような処理もここに書きましょう。

```ruby:app/controllers/users_controller.rb
def search
  @users = User.where('name LIKE(?)', "%#{params[:keyword]}%") #paramsとして送られてきたkeyword（入力された語句）で、Userモデルのnameカラムを検索し、その結果を@usersに代入する
  respond_to do |format| 
    format.json { render 'index', json: @users } #json形式のデータを受け取ったら、@usersをデータとして返す そしてindexをrenderで表示する
  end
end
```

以下のように書くとよりすっきりします。

```ruby:app/controllers/users_controller.rb
def search
  @users = User.where('name LIKE(?)', "%#{params[:keyword]}%")
  render json: @users
end
```

## コントローラーで検索された内容をjsで表示できるようにする
さて、今@usersにはフォームに入力された語句と部分一致したユーザーの名前の情報が入っています。
この中から、一人ずつ名前を表示できるようにしたいですね。
ここから、一気に書いていきます。

まずは、ビューに検索結果を表示させる場所を作りましょう。
リストとして表示させたいので、ulタグを用意しておきます。

```html:app/views/index.html.erb
<input type=form id="form" placeholder="ユーザー名を入力してください" style="width: 250px;"/>
<!-- ここから追記 -->
<ul id="result">
<!-- この中に<li>デデデ大王</li>みたいに検索候補を追加していきます -->
</ul>
```

次に、ulタグの中に検索結果を表示させるコードを書いていきます。
まず、.doneを書きます。
意味としては、「ajax通信が成功したら」、という意味だと考えて良いでしょう。
そして、dataという形で、コントローラーで作成した@usersがjｓに渡されています。
それを、each文で一つずつ取り出し、名前を表示させます。

```javascript:app/assets/javascript/user.js
$(document).on('turbolinks:load', function(){
    $(document).on('keyup', '#form', function(e){
    e.preventDefault();
    var input = $.trim($(this).val());
    $.ajax({
      url: '/users/search',
      type: 'GET',
      data: ('keyword=' + input),
      processData: false,
      contentType: false,
      dataType: 'json'
    })
    //ここから追記
    .done(function(data){ //データを受け取ることに成功したら、dataを引数に取って以下のことする(dataには@usersが入っている状態ですね)
      $('#result').find('li').remove();  //idがresultの子要素のliを削除する
      $(data).each(function(i, user){ //dataをuserという変数に代入して、以下のことを繰り返し行う(単純なeach文ですね)
        $('#result').append('<li>' + user.name + '</li>') //resultというidの要素に対して、<li>ユーザーの名前</li>を追加する。
      });
    })
  });
});
```

これで全ての実装が終わりました！
見た目はしょぼいですが、以下のように表示されていれば成功です！
![スクリーンショット 2017-05-17 21.27.01.png](https://qiita-image-store.s3.amazonaws.com/0/177591/cd61bc31-9084-729d-42cd-1614419e8f9d.png "スクリーンショット 2017-05-17 21.27.01.png")


`('#result').find('li').remove();`という記述をしているのは、同じユーザーネームでも、文字を打つたびに検索結果が追加されていってしまうからです。
例えば、'aaaaa'という名前のユーザーがいるとします。

```
aと入力→
aaaaa
と表示される
```

```
aaと入力→
aaaaa
aaaaa
と表示される
```

```
aaaと入力→
aaaaa
aaaaa
aaaaa
と表示される
```

実際に試してみると、そのような挙動をするはずです。
これを回避するために、このような記述が必要なのです。

## 最後に
最後まで読んでくださってありがとうございます！
インクリメンタルサーチと聞くと、それだけで難しい気がしてしまいますが、実際に行っていることを一つずつ追ってみると、以外と単純だったりします。

```
文字が入力される
↓
jsで読み取る
↓
コントローラーにjson形式でajax通信でデータを渡す
↓
そのデータを元に、like句を使って検索
↓
検索結果をjsに返す
↓
受け取ったデータをappendで表示する
```

私自身初心者ですが、わからないときは1から順に片付けていくと良いみたいですね！

## 参考資料
記事中では全く解説しなかったものについて、解説してくれているサイトたちです。

scaffold(スキャフォールド)について
[覚えておくと超便利！Ruby on Railsのscaffoldの使い方【初心者向け】](https://techacademy.jp/magazine/7204)

Turbolinksについて
[Turbolinks 5とTurbolinks Classic（2.x）の比較](http://qiita.com/jkr_2255/items/fe1e69e043c94a5c0a79)

preventDefault()について
[preventDefault()について
](http://www.koikikukan.com/archives/2013/11/21-023333.php)

ajaxについて
[はじめてajaxを使うときに知りたかったこと](http://qiita.com/nekoneko-wanwan/items/bedc0e826c0842ca0b11)

jsonとは
[RailsでJSON形式のデータを取得し、結果を画面に表示させる](http://qiita.com/nishina555/items/bee6dac44ce0ffaf3b93)

collection doについて
[Rails のルーティング](https://railsguides.jp/routing.html#%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B)

LIKE句について
[Railsであいまい検索(LIKE)やAND検索を行う](http://blog.codebook-10000.com/entry/20140413/1397363850)