0
0

More than 3 years have passed since last update.

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

Posted at

インクリメンタルサーチと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'が導入されていなければ導入します。
【例】

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');
    });   #〜追加
  });
});
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0