Ransackの検索結果をajaxで動的に反映させる方法

ransackという便利な検索用のgemがありますよね

参考：[Ransackのススメ](http://qiita.com/nysalor/items/9a95d91f2b97a08b96b0)

複雑な条件の検索をわざわざ自分で書かなくても簡単に実装できます.

ハッピーですね:blush:

これをajaxを使ってページ遷移せずに検索結果を反映させることができれば

もっとハッピーですね:blush::blush:

![qiita.gif](https://qiita-image-store.s3.amazonaws.com/0/121571/f0bf1e56-8bca-00df-9492-7c812a4545d0.gif)

ストレスのない検索に加えて、裏側で処理することでransackあるあるの汚いパラメータを見なくて済むこともメリットの1つだと思います！

今回はサンプルアプリケーションを作ってみます。

## 環境

* Rails 5.0.4
* ruby 2.3.3

## サンプルアプリケーション作成

### rails new

```bash:terminal
$ rails new ajax-ransack
$ cd ajax-ransack
```
### Gemfile編集

Gemfileに以下を追記する

```:Gemfile
gem 'ransack' # 言うまでもなくransack
gem 'haml-rails'# htmlを楽に書くために使います。
gem 'faker' # ダミーデータ作成に使います。
```

```bash:terminal
$ bundle
```

### UserのModelなど作成

```bash:terminal
$ rails g scaffold User name:string email:string description:text age:integer　sex:string
$ rails db:migrate
```

適当に`name,email,description,age`などのカラムを持ったユーザーを作成します。

### ダミーデータ作成

検索してなんぼですので、ダミーデータを作成します。

参考：[fakerとforgeryを使ってダミーデータを生成する](http://qiita.com/saino-katsutoshi/items/29c0918e489b27df637a)

```seeds.rb
100.times do
  User.create(name:Faker::Name.name,email: Faker::Internet.email,description:Faker::Job.title,age:Faker::Number.between(1,100),sex:Faker::Demographic.sex)
end
```

```bash:terminal
$ rails db:seed
```

これでそれらしきデータは入りました:sunglasses:

### Controller

```users_controller.rb
...

def index
  @q=User.search(params[:q])
  @users=@q.result
  # ajaxで送られた場合にはjsonを変えす
  unless params[:q].blank?
    render json: @users.select("id").map { |e| e.id  }.to_json
  end
end

...
```

## View

```index.html.haml
%h1 Listing users

=search_form_for @q,remote: true,:id=>"user_form" do |f|
  名前
  =f.text_field :name_cont
  男性
  =f.radio_button :sex_in_any,"Male"
  女性
  =f.radio_button :sex_in_any,"Female"
  email
  =f.text_field :email_cont
  ○歳以上
  =f.number_field :age_gt
  =f.submit

:coffee
  $ ->
    -# テキストフィールの変更を察知してsubmit
    $("#user_form").keyup ->
      $("#user_form").find("input[type='submit']").click()
    -# フォームのinputタグの変更をsubmit
    $("#user_form").change ->
      $("#user_form").find("input[type='submit']").click()
    -# ajaxの結果を受け取って実行する
    $('#user_form').on 'ajax:success', (event, results) ->
      $select=$("#user_check")
      $trs=$select.find("tr")
      $trs.each ->
        # idを読み取りInt型に変換する
        value=parseInt($(this).find("td").first().text())
        -# idが結果に含まれていたら表示する
        if value in results
          $(this).show()
        else
          $(this).hide()

%table
  %thead
    %tr
      %th id
      %th Name
      %th Email
      %th Description
      %th Age
      %th sex
      %th
      %th
      %th

  %tbody#user_check
    - @users.each do |user|
      %tr
        %td= user.id
        %td= user.name
        %td= user.email
        %td= user.description
        %td= user.age
        %td= user.sex
        %td= link_to 'Show', user
        %td= link_to 'Edit', edit_user_path(user)
        %td= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' }

```

#### 解説

そもそもsubmitボタンを押す前に勝手に検索結果を反映させる方がかっこいいと思ったのでそうしてます。

### keyupでテキストフィールドの変更を取得
```coffee
$("#user_form").keyup ->
   $("#user_form").find("input[type='submit']").click()
```
### changeでラジオボタンの変更を取得
```coffee
$("#user_form").change ->
      $("#user_form").find("input[type='submit']").click()
```
ラジオボタン以外にも全てのinput要素に対して反応します。

***テキストフィールドの場合、フォーカスが外れないと反応しないためリアルタイム性を追究するのであれば、keyupを利用することになります。***

### ajaxの結果を受け取る

```coffee
$('#user_form').on 'ajax:success', (event, results) ->
```
こんな感じでajaxの結果を受け取ります。今回はresultsの中にidの配列が入っています。

そんでもってidが含まれていたら`show()`なければ、`hide()`という感じでやっています！

今回の実装は絞込に重きを置いてるため、idだけのやり取りとなっており、要素自体はページを最初に開いたときのを利用しています。

必要なデータだけを取りだす場合は、show(),hide()ではなく、一回全部中身を消した後に、appendとかしていく感じになるかなと。

## サンプルのリンク
https://github.com/erbowl/ransack-ajax

![qiita.gif](https://qiita-image-store.s3.amazonaws.com/0/121571/f0bf1e56-8bca-00df-9492-7c812a4545d0.gif)

ご指摘、真摯に受け止めますのでぜひぜひよろしくお願いします〜！