##インクリメンタルサーチと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
【例】
// 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
class AddIndexToTweets < ActiveRecord::Migration
def change
add_index :tweets, :text, length: 32
end
end
ターミナル
$ rails db:migrate
##ルーティングなどAPI側の準備
アクションの中でHTMLとJSONなどのフォーマット毎に条件分岐するため、respond_toを使用します。
【例】
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ファイルを作成します。
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
テキストフィールドの作成
【例】
<%= 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ファイルを作成します。
$(function() {
$(".yyyy").on("keyup", function() {
var input = $(".yyyy").val();
});
});
##イベント時に非同期通信できるように
【例】
$(function() {
$(".yyyy").on("keyup", function() {
var input = $(".yyyy").val();
$.ajax({ #追加〜
type: 'GET',
url: '/tweets/searches',
data: { keyword: input },
dataType: 'json'
}) #〜追加
});
});
##非同期通信の結果を得て、HTMLを作成
非同期通信の結果を元にビューを生成します。
【例】
$(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関数を定義します。
記述内容は同じです。
【例】
<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>
【例】
$(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("一致するツイートがありません");
}
})
});
});
##エラー時の処理
$(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');
}); #〜追加
});
});