7つのアクションのみのインクリメンタルサーチ

##インクリメンタルサーチと7つのアクションのみのインクリメンタルサーチとの相違点
主に編集するファイル場所の違いと、それによるリクエストパスの違いになります。
1.検索に使用するコントローラーとビューの「名前」「場所」が異なる
2.構造が異なるため、ルーティングも異なる
3.ルーティングが異なるため、検索のリクエストパスが異なる
などの違いがあります。
##コントローラーとビューの「名前」「場所」が異なる
7つのアクションのみで検索機能を実装した場合、searches_controller.rbの作成にnamespaceを利用します。
そのため、コントローラーが格納されているディレクトリの構造が変わります。
app/controllers/tweets/searches_controller.rb
加えて、作成したsearches_controller.rbのindexアクションに対応するビューは、
app/views/tweets/searches/にindex.html.erbとして格納されます。
これらの「namespaceを利用したことによる構造と名前の違い」があります。
##検索のリクエストパスが異なる
ルーティングが変更されるため、検索機能を呼び出すリクエストを送信する処理を記述するJSファイルで、指定するリクエストパスを変更します。
##jQueryの導入
jQueryにgem 'jquery-rails'が導入されていなければ導入します。
【例】

```Gemfile
gem 'jquery-rails'
```
ターミナル

```
bundle install
```
【例】

```app/assets/javascripts/application.js
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery  #追加
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree 
```
textカラムにインデックスを設定します。
ターミナル

```
$ rails g migration AddIndexToTweets
```
```db/migrate/20XXXXXXXXXXXX_add_index_to_tweets.rb
class AddIndexToTweets < ActiveRecord::Migration
  def change
    add_index :tweets, :text, length: 32
  end
end
```
ターミナル

```
$ rails db:migrate
```
##ルーティングなどAPI側の準備
アクションの中でHTMLとJSONなどのフォーマット毎に条件分岐するため、respond_toを使用します。
【例】

```app/controllers/tweets/searches_controller.rb
class Tweets::SearchesController < ApplicationController
  def index
    @tweets = Tweet.search(params[:keyword])
    respond_to do |format|
      format.html
      format.json
    end
  end
end
```
app/views/tweets/searches/のディレクトリにindex.json.jbuilderファイルを作成します。

```app/views/tweets/searches/index.json.jbuilder
json.array! @tweets do |tweet|
  json.id tweet.id
  json.text tweet.text
  json.image tweet.image
  json.user_id tweet.user_id
  json.nickname tweet.user.nickname
  json.user_sign_in current_user
end
```
## テキストフィールドの作成
【例】

```index.html.erb
<%= form_with(url: search_tweets_path, local: true, method: :get, class: "xxxx") do |form| %>
  <%= form.text_field :keyword, placeholder: "投稿を検索する", class: "yyyy" %>
  <%= form.submit "検索", class: "zzzz" %>
<% end %>
<div class="aaaa">
  <% @tweets.each do |tweet| %>
    <%= render partial: "tweet", locals: { tweet: tweet } %>
  <% end %>
  <%= paginate(@tweets) %>
</div>
```
##テキストフィールドが入力されるたびにイベントが発火させる
app/assets/javascripts/のディレクトリにsearch.jsファイルを作成します。

```app/assets/javascripts/search.js
$(function() {
  $(".yyyy").on("keyup", function() {
    var input = $(".yyyy").val();
  });
});
```
##イベント時に非同期通信できるように
【例】

```app/assets/javascripts/search.js
$(function() {
  $(".yyyy").on("keyup", function() {
    var input = $(".yyyy").val();
    $.ajax({   #追加〜
      type: 'GET',
      url: '/tweets/searches',
      data: { keyword: input },
      dataType: 'json'
    })   #〜追加
  });
});
```
##非同期通信の結果を得て、HTMLを作成
非同期通信の結果を元にビューを生成します。
【例】

```app/assets/javascripts/search.js
$(function() {
  $(".yyyy").on("keyup", function() {
    var input = $(".yyyy").val();
    $.ajax({
      type: 'GET',
      url: '/tweets/searches',
      data: { keyword: input },
      dataType: 'json'
    })
    .done(function(tweets) {   #追加〜
      $(".contents.row").empty();
      if (tweets.length !== 0) {
        tweets.forEach(function(tweet){
          appendTweet(tweet);
        });
      } else {
        appendErrMsgToHTML("一致するツイートがありません");
      }
    })   #〜追加
  });
});
```
tweetsに投稿の情報が入っている場合のappendTweet関数と、
tweetsに投稿の情報が入っていない場合のappendErrMsgToHTML関数を定義します。
記述内容は同じです。
【例】

```_tweethtml.erb
<div class="content_post" style="background-image: url(<%= tweet.image %>);">
  <div class="more">
    <span><%= image_tag 'arrow_top.png' %></span>
    <ul class="more_list">
      <li>
        <%= link_to "詳細", tweet_path(tweet.id), method: :get %>
      </li>
      <% if user_signed_in? && current_user.id == tweet.user_id %>
        <li>
          <%= link_to '編集', "/tweets/#{tweet.id}/edit", method: :get %>
        </li>
        <li>
          <%= link_to '削除', "/tweets/#{tweet.id}", method: :delete %>
        </li>
      <% end %>
    </ul>
  </div>
  <%= simple_format(tweet.text) %>
  <span class="name">
    <a href="/users/<%= tweet.user_id %>">
      <span>投稿者</span><%= tweet.user.nickname %>
    </a>
  </span>
</div>
```
【例】

```app/assets/javascripts/search.js
$(function() {

  var search_list = $(".aaaa");   #追加〜

  function appendTweet(tweet) {
    if(tweet.user_sign_in && tweet.user_sign_in.id == tweet.user_id){
      var current_user = `<li>
                            <a href="/tweets/${tweet.id}/edit" data-method="get" >編集</a>
                          </li>
                          <li>
                            <a href="/tweets/${tweet.id}" data-method="delete" >削除</a>
                          </li>`
    } else {
      var current_user = ""
    }

    var html = `<div class="content_post" style="background-image: url(${tweet.image});">
                  <div class="more">
                    <span><img src="/assets/arrow_top.png"></span>
                    <ul class="more_list">
                      <li>
                        <a href="/tweets/${tweet.id}" data-method="get" >詳細</a>
                      </li>
                      ${current_user}
                    </ul>
                  </div>
                  <p>${tweet.text}</p><br>
                  <span class="name">
                    <a href="/users/${tweet.user_id}">
                      <span>投稿者</span>${tweet.nickname}
                    </a>
                  </span>
                </div>`
    search_list.append(html);
  }

  function appendErrMsgToHTML(msg) {
    var html = `<div class='name'>${ msg }</div>`
    search_list.append(html);
  }   #〜追加

  $(".yyyy").on("keyup", function() {
    var input = $(".yyyy").val();
    $.ajax({
      type: 'GET',
      url: '/tweets/searches',
      data: { keyword: input },
      dataType: 'json'
    })
    .done(function(tweets) {
      search_list.empty();
      if (tweets.length !== 0) {
        tweets.forEach(function(tweet){
          appendTweet(tweet);
        });
      } else {
        appendErrMsgToHTML("一致するツイートがありません");
      }
    })
  });
});
```
##エラー時の処理

```app/assets/javascripts/search.js
$(function() {

  var search_list = $(".aaaa");

  function appendTweet(tweet) {
    if(tweet.user_sign_in && tweet.user_sign_in.id == tweet.user_id){
      var current_user = `<li>
                            <a href="/tweets/${tweet.id}/edit" data-method="get" >編集</a>
                          </li>
                          <li>
                            <a href="/tweets/${tweet.id}" data-method="delete" >削除</a>
                          </li>`
    } else {
      var current_user = ""
    }

    var html = `<div class="content_post" style="background-image: url(${tweet.image});">
                  <div class="more">
                    <span><img src="/assets/arrow_top.png"></span>
                    <ul class="more_list">
                      <li>
                        <a href="/tweets/${tweet.id}" data-method="get" >詳細</a>
                      </li>
                      ${current_user}
                    </ul>
                  </div>
                  <p>${tweet.text}</p><br>
                  <span class="name">
                    <a href="/users/${tweet.user_id}">
                      <span>投稿者</span>${tweet.nickname}
                    </a>
                  </span>
                </div>`
    search_list.append(html);
   }

  function appendErrMsgToHTML(msg) {
    var html = `<div class='name'>${ msg }</div>`
    search_list.append(html);
  }

  $(".yyyy").on("keyup", function() {
    var input = $(".yyyy").val();
    $.ajax({
      type: 'GET',
      url: '/tweets/search',
      data: { keyword: input },
      dataType: 'json'
    })
    .done(function(tweets) {
      search_list.empty();
      if (tweets.length !== 0) {
        tweets.forEach(function(tweet){
          appendTweet(tweet);
        });
      } else {
        appendErrMsgToHTML("一致するツイートがありません");
      }
    })
    .fail(function() {   #追加〜
      alert('error');
    });   #〜追加
  });
});
```