[Rails, jQuery]インクリメンタルサーチ+αを実装

[Rails, jQuery]インクリメンタルサーチ+αを実装:


はじめに

現在プログラミングスクールにて学習中です。

カリキュラムでchatアプリの開発をしていて、インクリメンタルサーチをajaxを使用し、実装するところでハマった部分があったので記録に残そうと思います。


chatアプリ概要

今回作成しているchatアプリでは、groupsテーブル、usersテーブル、messagesテーブルがあり、groupsとusersは多対多の関係です。

中間テーブルはgroup_usersとしていてgroup_idとuser_idで紐づいています。

インクリメンタルサーチはgroupの新規作成とgroup編集で使用します。

メンバー検索欄に文字を入力するとuserの候補がでてくる仕様です。


そもそもインクリメンタルサーチとは

Googleなどで検索欄に文字を打つと色々候補が出て来ると思いますが、それのことです。


対象読者

インクリメンタルサーチの実装はできているが、group新規作成でcurrent_userを検索候補で非表示にしたい方。

group編集でcurrent_user含め既にgroupに所属しているuserを検索候補から非表示にしたい方。


group新規作成について

group新規作成の際にcurrent_userを検索で表示しないようにします。

最初からグループに参加していてほしいユーザーになるので、検索する時にcurrent_userを参加させますか?と出てきたら変ですよね。

それを防ぐためにcontrollerで処理をしてあげます。

users_controller.rb
def index 
  @users = User.where('(name LIKE(?) and (id != ?)', "%#{params[:keyword}%", "#{current_user.id}") 
  respond_to do |format| 
   format.json 
  end 
end 
LIKE句でUserモデルから曖昧検索をします。

name LIKE(?)の?部分にparams[:keyword]が入ります。

これで曖昧検索はOKです。

where文はandやorで条件を繋げることができるので、and (id != ?)と書き、#{current_user}を?に入れてあげます。

これでcontrollerの記述は終わりです。

_form.html.haml
#chat-group-users 
  #chat-group-user-22.chat-group-user.clearfix 
    %input{ name: "group[user_ids][]", type: "hidden", value: "#{current_user.id}" }  
    %p.chat-group-user__name 
       = current_user.name 
上記のようにしておけばグループ新規作成で初めからログイン中のメンバーが表示されるようになります。



スクリーンショット 2019-01-06 11.42.05.png


このようになればOKです。


group編集について ①

続いてグループ編集です。

下の画像のようにグループに参加しているメンバーは全員表示されていてチャットメンバーを追加部分に出てこないようにします。



スクリーンショット 2019-01-06 11.43.21.png


まず①では、チャットメンバーのところに既にグループ参加済みのユーザー名を表示させます。

グループのメンバーなのでgroups_controllerに記述します。

groups_controller.rb
#追加部分は@members 
def new 
 @group = Group.new 
 @group.users << current_user 
 @members = @group.users 
end 
#省略 
#追加部分は@members 
def edit 
 @group = Group.find(params[:id]) 
 @members = @group.users 
end 
 
groupに所属しているユーザーをインスタンス変数にいれます。

次はhtmlを修正します。

_form.html.haml
# each文追加 
- members.each do |member| 
  #chat-group-users 
    #chat-group-user-22.chat-group-user.clearfix 
# inputのvalueとcurrent_user.nameだった部分を変更 
      %input{ name: "group[user_ids][]", type: "hidden", value: "#{member.id}" }  
      %p.chat-group-user__name 
         = member.name 
groupメンバーを1人ずつ表示しなければならないのでeach文で表示してあげます。

このままだとmembersなんて無いよとエラーがでるのでrenderしているhtmlも変更します。

edit.html.haml
#修正前 
= render partial: 'form', locals: { group: @group } 
#修正後 
= render partial: 'form', locals: { group: @group, members: @members }  
new.html.haml
#修正前 
= render partial: 'form', locals: { group: @group } 
#修正後 
= render partial: 'form', locals: { group: @group, members: @members } 
これでチャットメンバーの部分はOKだと思います。


グループ編集について②

次はインクリメンタルサーチの部分ですね。

考え方としては

・編集するgroupのidをhtmlからjsに送れるようする。

・jsでgroupのidを取得しajaxでcontrollerに送れるようにする。

・controllerで既に所属しているユーザーを非表示にできるように省く。

新規作成のcurrent_userを省く時とほぼ同じなのですが、同じcontroller,jsなどを使用するので非常に難しかったです。

まずはhtml

_form.html.haml
= f.text_field :name, class: "chat__group_name chat-group-form__input", placeholder: "グループ名を入力してください" 
= f.hidden_field :id, class: "chat__group_id", value: group.id 
文字を入力する検索欄にてhidden_fieldを使用しgroup.idを送るようにしてあげます。

class名はjsで取得できればいいので好きなのにしてください。

user.js
$("#user-search-field").on("keyup", function() { 
    var input = $("#user-search-field").val(); 
    var group_id = $('.chat__group_id').val(); 
    $.ajax({ 
      type: 'GET', 
      url: '/users', 
      data: { keyword: input, groupId: group_id }, 
      dataType: 'json' 
    }) 
 
3行目の処理でgroup.idをgroup_idという変数に代入し、7行目でgroupIdというparamsでcontrollerに送信します。

groupIdじゃなくてgroup_idでもいいと思います。

users_controller.rb
# findでgroupIdに一致するgroupのデータを代入します。 
@group = Group.find(params[:groupId]) 
# groupに所属しているuserのidを配列で取得 
@ids = @group.users.ids 
def index 
  @users = User.where('(name LIKE(?) and (id != ?)', "%#{params[:keyword}%", "#{current_user.id}") 
  respond_to do |format| 
   format.json 
  end 
end 
2行追加で記述しgroupに所属しているユーザーを取得できました。

users_controller.rb
# 追加記述前 
@users = User.where('(name LIKE(?) and (id != ?)', "%#{params[:keyword}%", "#{current_user.id}") 
 
# where.notを追加記述します。 
@users = User.where.not(id: @ids).where('(name LIKE(?)) and (id != ?)', "%#{params[:keyword]}%", "#{current_user.id}") 
これで編集画面のインクリメンタルサーチも完成!なんですが、このままだとグループ新規作成でエラーが出るようになってしまいます。

新規作成でgroup.idは存在していないので、何だそれは?というエラーです。

そこでifを使って条件分岐します。

users_controller.rb
def index 
    # そもそもgroupのidがあるのか?という条件分岐です。 
    if params[:groupId].present? 
            #group.idがあればの処理 
      @group = Group.find(params[:groupId]) 
      @ids = @group.users.ids 
      @users = User.where.not(id: @ids).where('(name LIKE(?)) and (id != ?)', "%#{params[:keyword]}%", "#{current_user.id}") 
    else 
           # group.idがなければcurrent_userだけ省く 
      @users = User.where('(name LIKE(?)) and (id != ?)', "%#{params[:keyword]}%", "#{current_user.id}") 
    end 
    respond_to do |format| 
      format.json 
    end 
  end 
 
これで完成です。


 最後に

一応動作は思っていた通りになりました。

コードは汚いですが、、、、

where.notとwhereの部分とか・・・

あとindexアクションに色々書いているのでmodelに書いたほうがいいのかなーとも思ったりしてます。

users_controllerでGroupモデルからデータを取得しているのでそれも違和感ですね。

そもそもajaxとかjsの書き方がよくわかっていない中での実装だったので苦戦しました。

特に編集画面のところはどうやってやればいいんだとかなり悩んで苦しい思いをした箇所です。

ただ、ajaxを学習してJS面白いなーと思ったので引き続き学んで書いていきたいと思います!

もちろんRuby、Railsも!

この記事が少しでも誰かの役に立てば良いなと思います!

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)