Rails インクリメンタルサーチ

####はじめに
今回はインクリメンタルサーチについて書いていきたいと思います。
[Rails 無限スクロール（gem、ライブラリ無し）](https://qiita.com/223yuta/items/3be7fb632f3ef49d008e)の際に作成したサンプルアプリケーションに機能を追加していきます。（前提条件（無限スクロール実装前）の時点から作業開始）

####インクリメンタルサーチとは
アプリケーションにおける検索方法のひとつ。検索したい単語をすべて入力した上で検索するのではなく、入力のたびごとに即座に候補を表示させる。逐語検索、逐次検索とも。
>引用
>[Wikipedia](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)

####完成イメージ
![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1585805/b750e1e9-7f9b-0ef7-80e4-46d4217207cb.gif)


####インクリメンタルサーチ機能の作成
まずは普通の検索機能を作成し、その後インクリメンタルサーチ機能に変更していきたいと思います。

#####処理の流れ
1. #indexにてbooks一覧を表示
1. 検索機能の実装
1. 検索窓にキー操作を行うとイベント発火
1. #indexに検索キーワードを送信しレコードを取得
1. jbuilderにてjson形式に変換しデータを返却する
1. 返ってきたデータを元にjs側で処理

4.以降はjbuilderを利用しない方法でも実装可能です。[こちらの記事](https://qiita.com/223yuta/items/b2f70f729168e19c794c#%E3%82%82%E3%81%86%E4%B8%80%E3%81%A4%E3%81%AE%E5%8B%98%E9%81%95%E3%81%84)に記載しています。

#####実装
######1.#indexにてbooks一覧を表示
index.html.erbを一部partial化します。

```erb:app/views/books/index.html.erb
<h4>新規投稿</h4>
<div class='scroll__new'>
  <%= render 'new', book: @book %>
</div>

<h4>投稿一覧</h4>
<table class='scroll__table'>
  <thead>
    <tr>
      <th class='scroll__th'>タイトル</th>
      <th class='scroll__th'>内容</th>
    </tr>
  </thead>
  <tbody class='scroll__tbody'>
    <%= render 'index', books: @books %>
  </tbody>
</table>
```

```erb:app/views/books/_index.html.erb
<% books.each do |book| %>
  <tr>
    <td class='scroll__td'><%= book.title %></td>
    <td class='scroll__td'><%= book.body %></td>
  </tr>
<% end %>
```

######2.検索機能の実装
1.検索窓の作成

```erb:app/views/books/index.html.erb
<div class='search'>
  <%= form_with url: books_path, method: :get do |f| %>
    <%= f.text_field :key_word, placeholder:'キーワード', class:'search__word' %>
    <%= f.submit '検索'%>
  <% end %>
</div>
```

2.検索機能の作成

```ruby:app/models/book.rb
# 検索結果を返す
def self.search(key_word)
  if key_word == ''
    books = Book.all
  else
    books = Book.where(['title LIKE(?) OR body LIKE(?)', "%#{key_word}%", "%#{key_word}%"])
  end
end
```
titleとbodyの両方で検索できるようにしています。

```ruby:app/controllers/books_controller.rb
class BooksController < ApplicationController

  def index
    key_word = params[:key_word]
    @books = Book.search(key_word)
    @book = Book.new
  end
end
```

```erb:app/views/books/index.js.erb
$('.scroll__tbody').html('<%= j(render 'index', books: @books) %>');
```
 #indexには同期通信と非同期通信のどちらのリクエストも届きますので、index.html.erbとindex.js.erbを作成しています。わかりにくい場合はアクションを分けていただくと、分かりやすくなると思います。
以上で検索機能の作成は終了です。

######3.検索窓にキー操作を行うとイベント発火
ここからはインクリメンタルサーチを実装していきたいと思います。
検索窓にてinputイベントを検出したタイミングで処理を行います。

```js:app/assets/javascripts/search.js
$(document).on('turbolinks:load', function() {
  $(document).on('input', '.search__word', function(){
    // 処理を記述
  });
});
```

######4.#indexに検索キーワードを送信しレコードを取得
処理の中身を書いていきます。

```js:app/assets/javascripts/search.js
$(document).on('turbolinks:load', function() {
  $(document).on('input', '.search__word', function(){
    // tbodyの中身を一度空にする
    $('.scroll__tbody').html('');
    // 検索ワードの取得
    const key_word = $(this).val();
    // #indexにajax通信
    $.ajax({
      type: 'GET',
      url: '/books',
      data: {key_word: key_word},
      dataType: 'json'
    })
    .done(function(data){
      // 返ってきたdataに対して処理を実行
    })
  });
});
```

######5.jbuilderにてjson形式に変換しデータを返却する

```ruby:app/views/books/index.json.jbuilder
json.array! @books do |book|
  json.title book.title
  json.body book.body
end
```

jbuilderでの変換例は[こちら](https://github.com/rails/jbuilder)にいくつか載っていますが、検索すると記事もたくさん出てきますので、そちらの方がわかりやすいと思います。

######6.返ってきたデータを元にjs側で処理

```js:app/assets/javascripts/search.js
// .done(function(data)の中身
// 返ってきたdataに対して処理を実行
data.forEach(function(data){
  const html = `
    <tr>
      <td class='scroll__td'>${data.title}</td>
      <td class='scroll__td'>${data.body}</td>
    </tr>
  `;
  $('.scroll__tbody').prepend(html);
});
```

<details>
<summary>最終的なsearch.jsのcodeはこちら</summary><div>

```js:app/assets/javascripts/search.js
$(document).on('turbolinks:load', function() {
  $(document).on('input', '.search__word', function(){
    // tbodyの中身を一度空にする
    $('.scroll__tbody').html('');
    // 検索ワードの取得
    const key_word = $(this).val();
    // #indexにajax通信
    $.ajax({
      type: 'GET',
      url: '/books',
      data: {key_word: key_word},
      dataType: 'json'
    })
    .done(function(data){
      data.forEach(function(data){
        const html = `
          <tr>
            <td class='scroll__td'>${data.title}</td>
            <td class='scroll__td'>${data.body}</td>
          </tr>
        `;
        $('.scroll__tbody').prepend(html);
      });
    })
  });
});
```

</div></details>

以上でインクリメンタルサーチの機能実装は終了です。