No_ideaのわからん日和

✏️...プログラミングが全然出来ない奴がわからんわからん言いながら様々な構文(記述方法やエラー構文など)と奮闘しながら成長していく成長記録です🐢

Ruby on Rails: チャットメッセージ実装(自動更新機能まで)...chapter18-2

タイムゾーンの設定

2020年9月1日みたいな感じで投稿した日にちを表示したいのでタイムゾーンを設定していきます。

config/application.rb

module Rensyu1
 class Application < Rails::Application
  # Initialize configuration defaults for originally generated Rails version.
  config.load_defaults 6.0

  # Settings in config/environments/* take precedence over those specified here.
  # Application configuration can go into files in config/initializers
  # -- all .rb files in that directory are automatically loaded after loading
  # the framework and any gems in your application.
  config.time_zone = "Tokyo"
  config.active_record.default_timezone = :local
end

config.time_zone= "Tokyo"

config.active_record.default_timezone= :localを設定します。

$rails s

サーバーを再起動します。

https://techacademy.jp/magazine/22191#:~:text=%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%BE%E3%83%BC%E3%83%B3%E3%81%A8%E3%81%84%E3%81%86%E3%81%AE%E3%81%AF,%E3%81%8C%E3%81%9A%E3%82%8C%E3%81%9F%E3%82%8A%E3%81%97%E3%81%BE%E3%81%99%E3%80%82

 

 

 

②chat_messages_controller.rbの編集

app/controllers/chat_messages_controller.rb

def index
 @group = ChatGroup.find(params[:chat_group_id])
 @messages = ChatGroup.find(params[:chat_group_id]).chat_messages
end

@messagesにチャットグループでのメッセージを代入します。

 

 

③index.html.erbのビューを編集

https://gyazo.com/6781ccd0fa45a0b85d1f9b21fe9567f7

https://gyazo.com/89f484d0508d9edd20bf7de4dd206382

https://gyazo.com/0c6b1342f1c00691713c797fcafa2d7b

https://gyazo.com/7dd81829b992ee348ce8fd6122b89ce6

とりあえずメッセージが投稿されるようになりました。

しかし非同期通信はできていないので、投稿したときに

非同期通信をして部分的更新をして更新速度を上げるのと、2窓してみて自動更新できるようにしていきたいと思います。

 

④app/controllers/api/chat_messages_controller.rbを作成

$rails g controller api/chat_messages

https://gyazo.com/4eb0e8e76d30d2989d0adf1bbbbc99e1

class Api::ChatMessagesController < ApplicationController
end

これでApplicationControllerはApi::ChatMessagesControllerを継承できるようになりました。

これで、このコントローラーへコードしたことはapiを使用したもののみを対応させて動かすことができます。

後でここに投稿した後に投稿されたメッセージが表示されるように処理内容をコードします。

 

 

 

④create.json.jbuilder

app/views/chat_messages/create.json.jbuilder

json.id @conversations.id
json.user_icon @conversations.user.user_icon.url
json.message @conversations.message
json.image @conversations.image.url
json.date @conversations.created_at.strftime("%Y年%m月%d日")
json.updated_at @conversations.updated_at

app/controllers/chat_messages_controller.rbのcreateアクションでインスタンス変数へ代入したメッセージ情報をjsonへ変換します。

 

 

 

⑤index.json.jbuilder

app/views/api/chat_messages/index.json.jbuilder

json.array! @messages do |chat_message|
 json.id chat_message.id
 json.user_icon chat_message.user.user_icon.url
 json.message chat_message.message
 json.image chat_message.image.url
 json.date chat_message.created_at.strftime("%Y年%m月%d日")
 json.updated_at chat_message.updated_at
end

 忘れないようにこちらのjbuilderファイルも作成しておきましょう。

私はこのファイルを作成し忘れていて配列を用意したいのにできず2日悩みました。←慣れていれば、こんなことで悩むことはないと思うのですが😂 (笑)

 

 

 

⑥app/controllers/api/chat_messages_controller.rbを編集

class Api::ChatMessagesController < ApplicationController
 def index
  @group = ChatGroup.find(params[:chat_group_id])
  @messages = @group.chat_messages.includes(:user).where('id > ?', params[:id])
  respond_to do |format|
   format.html
   format.json
  end
 end
end

@groupのインスタンスへChatGroupモデルを通してfindメソッドでchat_group_idを探してきて、投稿しているチャットグループの情報を代入します。

@messages = @group.chat_messages.includes(:user).where('id > ?, params[:id]')

params[:id]...これはchat_messages.jsでajaxによって送られてきたidです。

チャットメッセージで最後に投稿されたメッセージのidよりも大きいidのメッセージがないか探してきてインスタンス変数@messagesへ代入します。

 

includes(:user)はN+1問題の対策のために使用します。

N+1問題を解決する理由としては同じ繰り返し処理を減らしてSQL発行を減らそうという考えです。

このincludesがなければ、「投稿されたメッセージは誰のなの???」とchatmessagesテーブルとusersテーブルの間を行ったり来たりして探すという作業が行われます。

なので投稿されたメッセージ分だけusersテーブルを行き来します。

これは無駄な作業です。

これではメッセージが増えれば増えるほどSQLへの負担が大きくなります。

投稿されたメッセージをまとめてusersテーブルへ探しに行けるのがincludesメソッドです。

https://qiita.com/TsubasaTakagi/items/8c3f4317ad917924b860

 

そしてrespond_to do |format|~endまででhtmlをjsonへ変換できるようにしておきます。

 

 

 

⑦chat_messages.jsファイルを作成

app/javascript下へchat_messages.jsファイルを作成します。

 

 

  

⑧app/javascript/packs/application.jsを編集

chat_messages.jsファイルを使用できるように↓を追加します。

require("chat_messages.js")

これでchat_messages.jsファイルを使用できるようになりました。

 

 

 

⑨app/controllers/chat_messages_controller.rbを編集

def create
 @conversations = ChatMessage.new(messages_params)
 if @conversations.save
  respond_to do |format|
   format.html
   format.json
  end
 end
end
 

createメソッドでjsonへ変換できるようにしておきます。

 

 

 

⑩app/javascript/chat_messages.jsを編集

注意することは、⑥でparams[:id]としたこと。

これは$.ajax({ ~ }).done({ ~ data: {id: 〇〇})←この太字にした部分と同じでないといけません。

なのでdata:{last_id: 〇〇}とした場合、⑥はparams[:last_id]とします。

[ 1 ]

 https://gyazo.com/99ef582eb08084f5ddb789763fa700c1

[ 2 ]

https://gyazo.com/a48a1d2c55da565e1158f03380732abc

[ 3 ]

https://gyazo.com/5e6ae49f2ca2da5dd68a189d152f501c

[ 1 ]でここでメッセージを投稿した時に、追加するhtmlを作っておきます。

・変数message_idに投稿されたメッセージのidを格納

・変数user_iconにもしユーザーアイコンがあるならばアイコンを、なければデフォルトのアイコンを格納

・変数chat_messageにメッセージがあるなら格納し、なければ入れない。

・変数imageもchat_messageと同様。画像があるかないかによって処理を分けてます。

・変数dateへupdated_atを格納

・変数htmlへ投稿したときの追加内容を格納

[ 2 ]でメッセージ投稿処理をしています。

・e.preventDefault()で投稿ボタンを押すまで処理を待ってくれます。

https://qiita.com/tochiji/items/4e9e64cabc0a1cd7a1ae

・変数formDataへformタグの開始から終了までの処理内容を入れてインスタンス変数を格納

・変数urlにメッセージページのurlを格納

・done(function(data){・・・})でdataは投稿された内容を格納しています。これを結果で返します。

$('form')[0].reset();...これでメッセージを投稿後リセットされます。

https://pikawaka.com/javascript/form-reset

$('#chat-messages-btn').prop('disabled', false);...ボタンが再度有効になります。

https://qiita.com/pugiemonn/items/5db6fb8fd8a303406b17

 

・変数iへ送ったメッセージが表示されているトップの座標を格納

・$('html, body').animate({scrollTop:i})...animateメソッドを使用しスクロール動作できるようにします。その際、垂直にスクロールしたいのでscrollTopを使用し垂直にスクロールするためのピクセル数を取得します。

https://itsakura.com/jquery-scroll

https://www.sejuku.net/blog/61689

[ 3 ]でメッセージを最新状態に保つための処理をしています。

・変数last_message_idに最新の(最後の)メッセージidを取得。

・if文内容でこのメッセージページと一致した場合にこの中の処理が実行できるようにします。このif文がないと他のチャットグループのメッセージを開いているとエラーがでます。

・変数insertHTMLへ空の変数を定義

・messages.forEach(function(message){})...messagesをforEachで繰り返しし取り出し処理して、この中での処理内容を引数messageへ渡します。

・insertHTML=buildHTML(message);...投稿されたメッセージ結果を格納

・$('.chat-messages__conversations').append(insertHTML);...insertHTMLに格納された処理内容を<div class="chat-messages__conversations">~</div>へ追加。

・setIntervalは一定の処理を繰り返すメソッドです。これで、自動更新機能を実装できます。7000は一定処理を繰り返す時間を設定しています(今回は7000ミリ秒=7秒間隔で処理しています)。

https://techacademy.jp/magazine/5537

 

[ 1 ]でのreturn htmlに関しては、これをコードしておかないと追加できなくなります。

returnは関数処理した結果を戻り値として返せます。

https://www.javadrive.jp/javascript/function/index4.html

https://gyazo.com/6704d1f3533a3d085fd1db3fd78cfa17

https://gyazo.com/7ef7c0e2efc4a874c12fcf0d6a36028d

 

 

 

 

 

非同期機能と自動更新機能を実装しました。

実装時のポイントはconsole.logを使用して処理内容を確認しながら作るってことでしょうか😅(例)console.log(this);)。

 

 

 

 

 

 

 

 

以上です。

Ruby on Rails: チャットメッセージ実装(フォームから投稿・保存まで)...chapter18-1

①チャットメッセージモデルを作成

$rails g model chat_message

https://gyazo.com/249510f902ab1ea526b27c5f2c82282f

 

 

マイグレーションファイルの編集&マイグレーションを実行

先ほど作成したマイグレーションファイルを編集します。

class CreateChatMessages < ActiveRecord::Migration[6.0]
 def change
  create_table :chat_messages do |t|
   t.text :message
   t.string :image
   t.references :user, foreign_key: true
   t.references :chat_group, foreign_key: true

   t.timestamps
  end
 end
end

メッセージ(messageカラム)はtext型で作成し、次の画像のみでも投稿できるようにしたいので、オプションは特につけません。

 

画像(imageカラム)はstring型で作成。メッセージ同様、メッセージだけでも投稿できるようにしたいので、オプションは特につけません。

 

ユーザー(user)をidで管理したいので、user_idを自動的に生成してくれるreferencesを使用しました。referencesを使うことによってforeign_keyが使用できるようになったのでtrueとして外部キー制約を使えるようにします。

これでメッセージを送った時に、ユーザーの情報(idやアイコン、ニックネームなど)が使用できるようになります。

 

チャットグループ(chat_group)をidごとに管理します。

これでチャットグループごとにメッセージが管理できるようになります。

ユーザー(user)同様にreferencesを使用し、chat_group_idを自動生成。

外部キー制約を使用するためにforeign_keyをtrue。

 

 

設定が終わったので、マイグレーションを実行します。

$rails db:migrate

https://gyazo.com/27915e30907f4bf12466282fa147b31d

 

 

 

③modelの編集

chat_message、user、chat_groupの各モデルを編集します。

それぞれの関係図:

https://gyazo.com/43848817a55b81b3507577d42885536b

一人のユーザーは沢山のグループを持つことができ、沢山のメッセージを書き込むことができます。

 

chat_message.rb

class ChatMessage < ApplicationRecord
 belongs_to :user
 belongs_to :chat_group
end

上の図の通り、一人の人が沢山のメッセージを書き込めるので、userモデルはbelongs_toを使用。

また、チャットグループも同様で、1つのチャットグループは沢山のメッセージを持っているのでチャットグループの関係性はbelongs_to。

 

 

chat_group.rb

class ChatGroup < ApplicationRecord
has_many :chat_group_to_users
has_many :users, through: :chat_group_to_users
has_many :chat_messages, dependent: :destroy

mount_uploader :chat_group_icon, ImageUploader
end

チャットグループは沢山のメッセージを持てるので、chat_messagesはhas_many。

また、dependentオプションでdestroyを設定することで、チャットグループを削除された時にメッセージも削除されます。

 

user.rb

class User < ApplicationRecord
 devise :database_authenticatable, :registerable,
  :recoverable, :rememberable, :validatable
has_many :tweets
has_many :chat_group_to_users
has_many :chat_groups, through: :chat_group_to_users
has_many :chat_messages, dependent: :destroy

mount_uploader :user_icon, ImageUploader
end

userモデルでもチャットグループと同様に1人のユーザーは沢山のチャットで沢山のメッセージを書き込めるのでchat_messageモデルとの関係性はhas_many。

また、dependentオプションでdestroyを設定することでユーザーを削除した時にメッセージも削除されます。

 

dependent: :destroyの使い方

▶︎ https://dorarep.page/articles/rails-dependent

 

 

 

④チャットメッセージコントローラーを作成

$rails controller chat_messages

https://gyazo.com/66193b134aada78b1d3e1103315d83e0

 

 

 

⑤ルーティングを設定

routes.rb

resources :chat_groups do
 resources :chat_messages, only: [:index, :create, :destroy]
end

chat_groupsと紐づけたいのでchat_messagesをネストしました。

また、メッセージページの表示、メッセージの作成、チャットグループを削除をできるようにしたいのでonlyメソッドを使用しindex, create, destroyの3つのアクションを追加できるようにしました。

このdestroyはチャットグループを削除したときに、このアクションがなければエラーが起こります。

理由としては、チャットグループを削除したのにメッセージが残っていると混乱が起きるためです。

 

 

 

⑥indexアクションをchat_messages_controllerへ追加

class ChatMessagesController < ApplicationController
 def index
 end
end

 

 

 

⑦index.html.erbを作成

app/views/chat_messages/index.html.erbを作成します。

 

 

 

⑧チャットグループ詳細ページからチャットページへのリンクを作成

$rails routes

https://gyazo.com/e0cbfeff734d704d26275c70eef99f98

/chat_groups/:chat_group_id/chat_messages(.:format)

がチャットメッセージのpathなので。。。

<div class="chat-group-show__talk-in">
 <%= link_to "トーク", "/chat_groups/#{@chat_group.id}/chat_messages" %>
</div>

@chat_group.idでchat_groups_controller.rbのshowアクションで取得したチャットグループ情報のidを取得しています。

 

 

 

⑨遷移できているか確認

遷移できるようになりました。

 

 

 

⑩コントローラーのindexアクションの処理内容を設定

app/controllers/chat_messages_controller.rb

def index
 @group = ChatGroup.find(params[:chat_group_id])
end

各チャットグループ情報をビューで使えるように編集します。

 

 

 

①①index.html.erbを編集(チャットグループへ戻るリンクを作成)

チャットメッセージページを作成します。

link_toメソッドでチャットグループへ戻れるようにリンクを作成しました。

メッセージ表示と投稿フォームを仮置きしています。

表示内容:

https://gyazo.com/2c24e1dc6141ad40b4a49f983628bda9

 

 

 

①②Carrierwaveを使用して、画像を保存できるようにする

app/models/chat_message.rb

class ChatMessage < ApplicationRecord
 belongs_to :user
 belongs_to :chat_group
 mount_uploader :image, ImageUploader
end

ユーザーアイコンやチャットグループアイコンの時に、Carrierwaveのgemはインストールして使えるようにしてあるので、modelに設定するところから始めます。

mount_uploaderでimageカラムを指定しています。

 

Carrierwaveについて

▶︎ https://qiita.com/nekotanku/items/5da43600f35eada64eac

 

 

 

①③コントローラー編集

投稿したフォームを保存できるようにcreateアクションとストロングパラメーターを作成し、処理内容をコードしていきます。

https://gyazo.com/7234c5d7d9159ba9693db1fd0bc9ef9d

createアクション

@coversations = ChatMessage.new(messages_params)でインスタンスを生成し、インスタンス変数(@conversations)へ代入。

 

ストロングパラメーターの内容

params.require(:chat_message).permit(:message, :image, :chat_group_id).merge(user_id: current_user.id)

 

params

送られてきたメッセージを受け取ります。

 

require(:chat_message)

送られてきた情報をモデルと紐付けます(今回はchat_messageモデルを使用してメッセージを保存します)。

 

permit(:message, :image, :chat_group_id)

permitで保存許可したい内容を記述します。

 

merge(user_id: current_user.id)

permitで許可されなかった情報を保存できるようにします。

 

ストロングパラメーターについて

▶︎ https://qiita.com/ozackiee/items/f100fd51f4839b3fdca8

 

 

 

①④font-awesome-railsをインストール、使用できるようにする

Font Awesomeで用意されているアイコンを使用したいのでfont-awesome-railsをインストールします。

gem 'font-awesome-rails'

$bundle install

app/assets/stylesheets/application.scss

@import "font-awesome";

これで使用できるようになりました。

 

font-awesome-railsについて

▶︎ https://pikawaka.com/rails/font_awesome_rails

 

🚨 font-awesome-railsは最新版font-awesome5には対応していないみたいです。

font-awesomeのversion5の一部のアイコンが表示されないので、対応させたい場合はfont-awesome-sassをインストールします。

gem 'font-awesome-sass'

$bundle install

app/assets/stylesheets/application.scss

@import 'font-awesome-sprockets';
@import 'font-awesome';

※絶対この順番にしてください。

入れ替えてしまうとアイコンが表示されなくなります。

$brew install yarn

※yarnをインストールをしていない場合はインストールします

 

 

brew

Homebrewのこと。

デフォルトでは利用できない便利なツールをインストールしたりアンインストールしたりを管理するツールのこと。

macOSのためにあるツールです。

 

Homebrewについて

▶︎ https://qiita.com/TAByasu/items/47c6cfbeeafad39eda07

 

yarn

Node.js(JavaScript)のパッケージマネージャのこと。

パッケージマネージャとは、コンピュータに何のソフトウェアがインストールされたかを記録し、新しいソフトウェアのインストール・新しいバージョンへのソフトウェアの更新・以前インストールしたソフトウェアの削除を容易に行えるようにするプログラム。

 

yarnについて

▶︎ https://www.sejuku.net/blog/72222#:~:text=Yarn%E3%81%AFNode.js(JavaScript,%E3%81%9C%E3%81%B2%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82

パッケージマネージャについて

▶︎ https://www.debian.org/doc/manuals/aptitude/pr01s02.ja.html#:~:text=%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%81%A8%E3%81%AF%E3%80%81%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF,%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A7%E3%81%99%E3%80%82

 

font-awesome(version5)はフロントエンド開発との連携機能が更に強化されて、手軽にJavaScriptのバンドルにフォントを埋め込むことができるようになっています。

 

font-awesome(v5)について

▶︎ https://qiita.com/riversun/items/4faa56ac40071f638313

 

 

yarnをインストールしたら、サーバーを再起動($rails s)

これでfontawesome(v5)が使えるようになりました。

 

 

 

①⑤index.html.erbを編集(投稿フォームを作成、保存)

https://gyazo.com/00118a1f84903b61cc7e57270259d036

投稿フォームから画像とメッセージが保存できるか確かめます。

注意しなければならないのは、

・text_field_tagのchat_message[chat_group_id]

・file_fieldのname: 'chat_message[image]'

・text_areaのname: 'chat_message[message]'

です。

これがparamsとして送られると、

:chat_message => {:chat_group_id => "〇〇", :image => "〇〇", :message => "〇〇" }

とハッシュ構造されて送られます。

実際に保存できるか確かめます。

https://gyazo.com/87fc9036ca4d4723c6e8fe7b0354446c

画像とメッセージを投稿してみます。

これで保存できるようになりました。

 

 

 

 

今回の実装で苦労したのは、フォームから投稿する時にchat_group_idは保存できているのに、メッセージと画像がparamsとして送られているのに保存できずに時間がかかったことです😅

途中でname属性でハッシュさせたらどうだろうと気づくことができました。

name属性の使い方の勉強になりました。

 

次回はチャットメッセージの実装の続きを書こうと思っています。

 

 

 

 

 

 

 

 

 

 

 

 以上です。

Ruby on Rails: チャットグループの修正...chapter17

エラーが出てしまったので修正しました。

 

▼ エラー内容及び原因 ▼

(エラー内容→原因)

①チャットグループの詳細画面へ遷移するとエラーが発生

→チャットグループ情報を取得するべきところをユーザーidを取得していた

②カレントユーザーが二重でグループに保存されてしまう

→chat_groups/new.html.erbでform_withでカレントユーザー情報(+グループ追加ユーザー情報)+text_field_tagでカレントユーザーidを送信と二重でカレントユーザー情報を送信していた

 

▼ 修正後のコード ▼

chat_groups_controller.rb

https://gyazo.com/b3ac396de74f66f970a3962eeb406bbe

・newアクションでcurrent_userを追加したのを削除。

・createアクションでcurrent_user情報を二重で保存しないように chat_group_new.user_ids = current_user.idを削除。

 

chat_groups/new.html.erb

https://gyazo.com/c95d12cf6fc9619f360ee5d2a47f837f

text_field_tagでカレントユーザー情報を送っていたのを削除しました。

 

chat_groups/show.html.erb

https://gyazo.com/2fe80805121575e423a7a9e567d01616

if文でチャットメンバー達がアイコンを登録しているかによって条件分岐するように修正。

 

 

これで、チャットメンバーの詳細画面へ遷移することができ、かつグループメンバーでチャットグループを作成したユーザーの二重登録をなくすことができました。

 

 

 

 

 

 

次回はチャットグループからメッセージを送る時に自動更新をして最新状態にして置けるように実装していきたいと思います。

 

 

 

 

以上です。

Ruby on Rails: インクリメンタルサーチと非同期通信...chapter16

今回は非同期通信、インクリメンタルサーチをやりたいと思います。

jQueryを使用します。

 

jQueryとは

Javascriptを扱いやすくしたものです。

Javascriptで10行くらいかけるものを1,2行で済ませてくれるのがjQueryです。

また、ブラウザによってJavascriptが動かない場合がありますが、jQueryではどのブラウザでも同じ動きをします。

 

 

Gemfile

jQueryが使用できるようにgemをインストールします。

$bundle install

app/javascript/users.jsファイル作成

application.js

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require('jquery')

require("users.js")

$rails s

これだけではエラーが出ました。

なので、解決策を検索。

https://qiita.com/masahisa/items/eaacb0c3b82f4a11fc13

$yarn add jquery

config/webpacker/enviroment.js

const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
})
)

module.exports = environment

$rails s

これでエラーがなくなりました。

 

yarnこれはRailsでいうところのGemみたいな役割をするJavascript版らしいです。

 

 

routes.rb

resources :users, only: [:edit, :update, :index] do
 collection do
  get 'users/user_page', to: 'users#user_page'
 end
end

indexアクションのルーティングを追加しました。

 

users_controller.rb

後ほど検索した結果をkeywordをキーとして受け取るようにします。

なのでparamsで受け取った検索がもし空ならnilで返します。

User.where()でUserモデルを使用し、whereでDBに検索をかけることができます。

検索したいのはnicknameなので"nickname"とします。

また"nickname LIKE?"とすることで"あいまい検索"ができるようになります。

?←これはプレースホルダーというもので、第2引数("%#{params[:keyword]}")を?のところに置き換えることができます。

つまりaaaというユーザーを検索したい時、「a」と入力するだけでaのつくユーザーを探してくれます。

しかし、aがどこについてもいいという検索は避けたいので(abc, bac, cbaなど)、ワイルドカードを使用し検索条件を設定します。

ワイルドカードは_と%があります。

%...0文字以上の任意の文字列

_...任意の1文字

%〇〇とするしてaのつくユーザーを検索します。

aのつく候補として(aaa, abc, bac, cba,aa,ab,ba)がいるとすると、aと入力すると

候補(aaa, cba, aa, ba)が出てきます。これは〇〇aとつくユーザーを検索できます。

次に〇〇%で検索してみます。

候補(aaa, abc, aa, ab)が出てきます。これはa〇〇とつくユーザーを検索できます。

次に_〇〇を試してみます。

候補(aa, ba)が出てきます。つまり○+aを検索できます。

%と違い_(アンダーバー)の数今回は1文字分を検索しているので、cbaは検索から弾かれます。

次に〇〇_を試します。

 

候補(aa, ab)が出てきます。a+○を検索しています。

今回はaと入力するとa○やa〇〇のように頭文字に検索したものの候補がくるようにしたいので〇〇%にしたいと思います。

https://www.wakhok.ac.jp/biblion/1994/DB/subsection2.4.3.5.html 

 

.where.not(id: current_user.id)

ログインユーザー(チャット新規作成しているユーザー)が検索されないようにwhere.not()を使いidを検索できないようにします。

@users = User.where("nickname LIKE?", "#{params[:keyword]}%").where.not(id: current_user.id)

そしてインスタンス変数@usersに代入します(検索結果を代入)。

respond_to do |format|
 format.html
 format.json
end

respond_to do |format|~end

ここに記述すると、通常はhtml形式で結果を取得するが他の形式で取得したい場合に指定できます。

チャット新規作成ページへ遷移した時はhtmlで取得したいのでformat.html。

そしてjsonを介してデータ(検索をかけたいユーザー)を取得する時はjsonを使用したいのでformat.jsonとしています。

 

app/views/users/index.json.jbuilder

json.array! @users do |user|
 json.id user.id
 json.nickname user.nickname
end

index.json.jbuilderファイルを作成します。

今回はjsonを使用したいと思います。

jsonとは"Javascript Object Notation"の略です。

〇〇.json.jbuilderファイルはjson形式を作成しやすくするために作成するファイルです。

このファイルに記述することによってjson形式の文字列のデータを作成することができます。

 

json.array!

インスタンス変数が複数あるときに使用します。

@usersの中身はユーザー情報が入っています。

例えば、aaaというユーザーの場合

https://gyazo.com/b524ee40f7612abfb59948b5a1ea9bf8

idやnickname、emailなど複数の情報が入っています。

 

json.キー名 "値"

jsonに変換したい(データ渡したい)のはidとnicknameなので、usersコントローラーで作成したindexアクションの中の処理で作成したインスタンス変数(@users)から繰り返し処理をして|user|一つずつuserを取り出ます。

そしてuser.idとuser.nicknameと記述してidとnicknameを取り出し、json形式でidとnicknameを使用できるようにidとnicknameをキー名として値を渡しています。

 

https://pikawaka.com/rails/jbuilder(jbuilderの使い方について)

 

 

app/views/chat_groups/new.html.erb

全体のコード:

https://gyazo.com/c420b3226691d57a3c0b2af0bf97eb55

検索フォーム部分のコード:

 https://gyazo.com/e43b87bc7504d87c3d9199a0a504be0a

検索した結果を

<div id="chat-group-new__member__search"></div>

↑部分に表示します。

 

https://gyazo.com/1a7f6fd9138f820a6cad55766ae69d8e

メンバー追加後:

<% @chat_group_new.users.each do |user| %> ~ <% end %>

ここのコードは検索して追加したユーザーが表示されるところです。

このコードが出現したり削除をクリックしたときに消えたりするようにします。

 

users.js

https://gyazo.com/385393aa49a1d79775ebc02bc07ed207

https://gyazo.com/9e88a345327134d97104af055aa3b903

$(function(){〇〇〇〇});

jQuery(document).ready(function(){〇〇〇〇});の略。

これはHTMLを読み込みが終わったら実行するという意味です。

 

function addUser(user){
 let html = `
  <div class="chat-group-new__member__search__users>
   <p class="chat-group-new__member__search__users--name">${user.nickname}</p>
   <div class="chat-group-new__member__search__users--add" data-user-id="${user.id}", data-user-name="${user.nickname}">
    追加
   </div>
  </div>
   `;
 $("#chat-group-new__member__search").append(html);
};

まず、関数を作成します。

関数とは、ある処理をまとめておけるようなものです。

関数を使用することでコードが見やすくなります。

関数を作成する時は、functionを最初に記述します。

続いてfunctionに任意の関数名を指定します。今回はaddUserにします。

()内に引数をします(引数user)。

ここに引数を指定しておくと、関数を呼び出されてた時に値が渡されている場合にその値を代入してくれます。後ほど、関数を呼び出すコードを記述します。

letは変数を宣言する時に使用します(これは変数ですよと分かりやすくしています)。

letを使用すると変数を再宣言できなくなります。

let a = '初期設定';

a = '再代入可能';

let a = '再宣言不可';

今回は変数htmlをletで宣言しました。

$(〇〇).append(html);で先ほど宣言した変数htmlを〇〇へ追加します。

 

function notUser(){
 let html =`
  <div class="chat-group-new__member__search__users">
   <p class="chat-group-new__member__search__users--none">一致するユーザーは見つかりません。</p>
  </div>`;
 $("#chat-group-new__member__search").append(html);
};

関数を作成します。

関数名notUserにし、今回は引数はなしなので()のみです。

letを使用し変数を宣言します。

そして、appendメソッドを使用し、ユーザーが見つからない時にid="chat-group-new__member__search"へ追加する関数を作成しておきます。

 

function removeUser(nickname, id){
 let html = `
  <div class="chat-group-new__add__right__user">
   <p class="chat-group-new__add__right__user--name">${nickname}</p>
   <a class="chat-group-new__add__right__user--remove-js add__user__remove--btn" data-user-id="${id}" data-user-name="${nickname}">削除</a>
  </div>`;
 $("#chat-group-new__add__right__js").append(html);
};

検索で追加したユーザーを表示するための処理をする関数を作成します。

上記と同様にappendで変数htmlの内容をid="chat-group-new__add__right__js"へ追加します。

 

function addMember(userId){
let html=`
<input name="chat_group[user_ids][]" type="hidden" value="${userId}" id="chat_group_user_ids_${userId}"/>`;
$("#chat-group-new__add__right__js").append(html);
};

https://gyazo.com/9f1db133b1cf2fa1d6ebc393af435825

ユーザーを追加した後に追加したユーザーを登録できるようにinputメソッドにユーザーidを入れて変数htmlに代入します。

そして、変数htmlをid="chat-group-new__add__right__js"へ追加できるように関数を作成します。

追加したユーザーの値を引き渡されるように引数を記述します(userId)。

上記のremoveUserは表示をするだけです。

このinputがないと新規作成ボタンを押した時にデータが保存できません。

 

 

$("#chat-group-new__members__right__field__js").on("keyup", function(){
 var input= $("#chat-group-new__members__right__field__js").val();
 $.ajax({
  type: 'GET',
  url: ' /users',gg
  data: {keyword: input},
  dataType: 'json'
 })
 .done(function(users){
  $("#chat-group-new__member__search").empty();
  if(users.length !==0){
   users.forEach(function(user){
   addUser(user);
  });
  } else if(input.length == 0){
   return false;
  } else {
   notUser();
  };
 })
 .fail(function(){
   alert("通信エラー");
 });
});

$(〇〇).on("keyup",function(){処理内容});

.on()これを使うと様々なイベント処理(マウス操作やキーボード入力、フォームを送信した時、画面をスクロールした時など)をすることができます。

https://www.sejuku.net/blog/38774

今回はkeyupを使用します。

keyupはキーボードを入力した時に発火するように(処理されるように)しています。

何を発火するのかというと、funciton(){var input=$("#chat-group-new__members__right__field__js").val()〜.fail(fucntion(){alert("通信エラー")});

};までです。

まず、varを使用し変数を宣言します。varはletとは異なり、値の再代入や再宣言ができます。

変数inputに.val()を使用して(ここに記述された値を取得します)、id="chat-group-new__members__right__field__js"を代入(つまり、検索をかけた時にその検索した内容の値を取得できます)。

↓ここのフォームに入力した値を取得

 

$.ajax({})

これはこれを記述することで画面を切り替えずに一部の情報をサーバーに送信して、情報を受け取ることができます。

type: リクエストタイプ(HTTP動詞)を決めます。GETかPOSTを選べますが、検索結果をただ表示したいだけなのでGETにしました。

url:送信先を指定します。

users GET    /users(.:format)              users#index

usersコントローラーのindexアクションへ送信したいのでurlである(/users)を記述します。

data:送信先へ送るデータをキー/値のペアで設定します。

keywordをキーとしてinputを値として送ります(つまり先ほどの変数inputで入力して取得した値)。

このデータはusersコントローラーのindexアクションへ渡されます。

dataType:サーバーから返ってくるデータの型を指定できます。

種類はjson,xml,htmlなどがあります。

今回はjsonを使用したいのでjsonを記述。

 

.done(処理内容)){

ここにデータの送受信が成功したときの処理を書きます。

$("#chat-group-new__member__search").empty();

.empty()を使用し、id="chat-group-new__member__search"の中を空にします(< id="chat-group-new__member__search">~</div>内)。

これにより、追加を押したら表示が消えます。

▼チャット参加メンバー▼の上のaaa 追加が消えます。

 

if(users.length !==0){
users.forEach(function(user){
addUser(user);
});

if文を使用してusers.lengthは検索をかけた時にユーザーが何件ヒットするかによって分岐しています。

ユーザーが0件ヒットでなければ該当があったら)、forEachメソッドを使用してヒットしたユーザーを繰り返し処理をします。

その繰り返し処理された内容を、関数(addUser(user))に値を引き渡します。

else if(input.length == 0){
 return false;
}

input.length(検索フォームに入力された値)が0ならば(入力を消した場合)、イベントを中断します。

else {
 notUser();
};

そうでなければ(ヒット件数0件)の場合は関数notUserを実行します。

.fail(処理内容))

ここにはデータの送受信を失敗したときの処理を記述します。

alert("通信エラー");

alert()メソッドを使用し、通信エラーとアラートを表示します。

 

$(document).on("click", ".chat-group-new__member__search__users--add", function(){
let userName = $(this).attr("data-user-name");
let userId = $(this).attr("data-user-id");
$(this).parent().remove();
removeUser(userName, userId);
addMember(userId);
});

$(document).on(処理内容)

このdocumentはhtml要素へ簡単にアクセスすることができます。

https://qiita.com/k152744/items/6453923550d5e4166557

console.logを使い、documentを見てみると...

https://gyazo.com/509e77874ce85173d89da109739f4ae4

今扱っているチャット新規作成ページのhtml要素を取得できてます。

そして、検索してヒットして「〇〇 追加」と表示され、この「追加」をclickしたらイベントが発火します。

let userName = $(this).attr("data-user-name");
let userId = $(this).attr("data-user-id");

変数をletで宣言します。

ここに記述してあるthisの中身↓

https://gyazo.com/b054fbfa854490c12f7e8d1c482c9429

これは追加をクリックした時のものです。

.attr()を使用し、このthisの中からdata-user-idとdata-user-nameを取得します。

そして取得した値を変数userName、userIdへ代入します。

https://techacademy.jp/magazine/26775(←.attr()について)

$(this).parent().remove();

このthisは↓

です。

.parent()は一つ上の階層の親要素を取得することができます。

つまり↓

let html = `
  <div class="chat-group-new__member__search__users>
   <p class="chat-group-new__member__search__users--name">${user.nickname}</p>
  <div class="chat-group-new__member__search__users--add" data-user-id="${user.id}", data-user-name="${user.nickname}">
   追加
  </div>
 </div>
`;

div class="chat-group-new__member__search__usersが親要素となります。

ここの要素から取得しています。

.remove()は$(〇〇).remove();の〇〇の要素を削除することができます。

よって、

↑を削除することができます。

.empty()と.remove()の違いは、emptyでは指定した要素の子要素のみが削除されます。

removeでは指定した要素の子要素まで削除されることになります。

つまりthisで取得しているものを削除したいのでremoveを使用します。

https://www.sejuku.net/blog/36261

https://uxmilk.jp/10889

 

removeUser(userName, userId);
addMember(userId);

そして変数に代入した値を引数を各関数へ渡します。

 

 

$(document).on("click", '.chat-group-new__add__right__user--remove-js', function() {
 $(this)
 .parent()
 .remove();
});

documentでhtml要素を取得し、「〇〇 削除」の削除をクリックした時にイベントを発火させます。

ここのthisは↓

https://gyazo.com/b776042932f00721eecce79ce8b30997

これを.parent()で一つ上の階層の親要素から取得します。

つまり↓

let html = `
 <div class="chat-group-new__add__right__user">
  <p class="chat-group-new__add__right__user--name">${nickname}</p>
  <a class="chat-group-new__add__right__user--remove-js add__user__remove--btn" data-user-id="${id}" data-user-name="${nickname}">削除</a>
 </div>`;

<div class="chat-grop-new__add__right__user">~</div>までを取得します。

.remove()で要素を削除します。つまりthis↓

を削除します。

 

app/views/layouts/application.html.erb

 https://gyazo.com/12fa3e76860b6a93791fa9ebe6f79585

チャット新規作成ページへ遷移した際にイベント発生しないのを防ぐためにbodyにdata-turbolinks="false" としておきます。

これでturbolinksが悪さをするのを防ぎます。

 

 

完成

https://gyazo.com/7688b4702266b4c0dbc763d75aecdd6c

 

 インクリメンタルサーチと非同期通信ができました。

 

 

 

 

 

 

 

以上です。

 

 

 

Ruby on Rails: チャットグループ機能(アイコン表示)の実装(CarrierWave使用)...chapter15

今日はusersテーブルとchat_groupsテーブルにアイコンを保存できるように各user_iconカラムとchat_group_iconカラムを追加したいと思います。

そして↓のようにユーザーページにアイコンを編集できるようにして、チャットグループでもアイコンを選べるようにして表示できるようにしていきたいと思います。

 

イメージ図:

 

やりたいこと

①DB設計(README)にアイコンを追加する

②カラム追加

③CarrierWaveの導入

④コントローラーの編集(chat_groups_controller.rb)

⑤ビューの編集(chat_groups/new.html.erb, edit.html.erb)

⑥コントローラーの編集(users_controller.rb)

⑦ルーティングの編集

⑧パスの変更

⑨ユーザーページの編集及び編集ページを作成

⑩ユーザーページのアイコンを表示

①①チャットグループへアイコンを表示

①②各アイコンを整える

 

①DB設計(README)にアイコンを追加する

## usersテーブル
|Column|Type|Options|
|------|----|-------|
|nickname|string|null: false|
|email|string|null: false|
|password|string|null: false|
|user_icon|string||
 
|Column|Type|Options|
|------|----|-------|
|chat_group_name|string|null: false|
|chat_group_description|text||
|chat_group_icon|string||

user_icon、chat_group_iconをstring型で追加します。

 

 

 

 

 

②カラム追加

各テーブルにアイコンを保存できるようにカラムを追加していきます。

最初はusersテーブルに追加していきます。

$rails g migration AddUser_iconToUsers user_icon:string

invoke  active_record

create    db/migrate/20200702130354_add_user_icon_to_users.rb

class AddUserIconToUsers < ActiveRecord::Migration[6.0]
 def change
  add_column :users, :user_icon, :string
 end
end

$rails db:migrate

https://gyazo.com/c86ff3c62fd5a9eaf928fc88c1e2adef

 

chat_groupsテーブルにも追加していきます。

$rails g migration AddChat_group_iconToChat_groups chat_group_icon:string

invoke  active_record

create    db/migrate/20200702131112_add_chat_group_icon_to_chat_groups.rb

class AddChatGroupIconToChatGroups < ActiveRecord::Migration[6.0]
 def change
  add_column :chat_groups, :chat_group_icon, :string
 end
end

$rails db:migrate

https://gyazo.com/09cb4fa9cf93f2b7ac75ebe0479af2f7

 

 

 

 

③CarrierWaveの導入

Gemfile

gem 'carrierwave'

$bundle install

$rails g uploader image

create  app/uploaders/image_uploader.rb

このファイルで保存方法を設定できる。

app/models/user.rb

class User < ApplicationRecord
 # Include default devise modules. Others available are:
 # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
 devise :database_authenticatable, :registerable,
 :recoverable, :rememberable, :validatable
 has_many :tweets
 has_many :chat_group_to_users
 has_many :chat_groups, through: :chat_group_to_users

 mount_uploader :user_icon, ImageUploader
end

mount_uploader :user_icon, ImageUploaderを追加。

 

app/models/chat_groups.rb

class ChatGroup < ApplicationRecord
 has_many :chat_group_to_users
 has_many :users, through: :chat_group_to_users

 
 mount_uploader :chat_group_icon, ImageUploader
end

mount_uploader :chat_group_icon, ImageUploaderを追加。

 

✍️ mount_uploader :(カラム名), (アップローダークラス)

modelに記述することにより画像をアップロードした際にImageUploaderクラスの設定を利用できるようになります。

アップロードされた画像はpublic/uploads下へ保存されます。

 

$rails s(再起動)

これで準備完了です。

 

参照:https://qiita.com/nekotanku/items/5da43600f35eada64eac

参照:https://qiita.com/ttaka66/items/264dcb85e41f9135685c

参照:https://pikawaka.com/rails/carrierwave

 

 

 

 

 

④コントローラーの編集(chat_groups_controller.rb)

app/controllers/chat_groups_controller.rb

https://gyazo.com/400d0245070037c7d38f6c7042832705

newメソッド

@chat_group_new.users << current_user

・current_user

ログインしているユーザー

・<<

尖っている方に開いている方の情報を付け加える(@chat_group_new.usersへ情報を付け加えている)

@chat_group_new

chat_groupsテーブルのインスタンス

・.users

.usersで@chat_group_newに所属しているusersテーブルのレコードを配列[]で取得できる。

これでログインしているユーザー(current_user)を@chat_group_new(chat_groupsテーブルのインスタンス).users)に加える(<<)ことができます。

 

https://gyazo.com/f2f6f12730330cdf45386fde83acf523

ストロングパラメーターに:chat_group_iconを追加。

 

 

 

 

 

 

⑤ビューの編集(chat_groups/new.html.erb,edit.html.erb)

先にチャットグループ新規作成でアイコンを保存できるようにしたいと思います。

https://gyazo.com/ff25eafa6a342e6903bb9e50284f79c4

 

file_fieldはファイル入力ボタンを作成します。

:chat_group_icon(カラム名)を指定して、今回はchat_group_iconカラムに保存できるようにしています。

http://localhost:3000/chat_groups/new

新規作成してみます。

 

 

次は編集ページでも画像をアップロードできるようにします。

https://gyazo.com/d5b0c16d4af5786991e79889786e4ca4

http://localhost:3000/chat_groups/8/edit

ファイルを選択できるようになったので編集してみます。

編集前:

編集後:

icon_kari.png → tsuki.png へ変更できているので成功です!

 

 

 

 

⑥コントローラーの編集(users_controller.rb)

https://gyazo.com/3624de02731658ae98959c22c20132d6

 

ユーザーページから『アカウント設定』リンクをはり、ニックネームとアイコンを編集できるようにしたいので、user_pageでログインしているユーザーのidを取得し、@user_infoに代入。

editメソッドでもユーザーのidを取得しインスタンス変数へ代入することで編集ページへ情報を渡せるようにしました。

そしてupdateメソッドでも同様にユーザー情報をedit_userへ代入し、ストロングパラメーター を通して、updateメソッドで編集した内容を保存できるようにしました。

ストロングパラメーターで許可したのは編集できるようにしたいnicknameとuser_iconをpermitに設定しました。

 

 

 

 

 

⑦ルーティングの編集

https://gyazo.com/4b069503182affd9b264961a63918bac

resourcesを使って一括に設定。

その中でも今回使うのはeditアクションとupdateアクションなのでonly: [:edit, :update]とすることでこの2つのルーティングを設定しました。

また、user_pageはonlyで設定できないので、追加したいアクションを記述できるcollectionを使ってブロック内に記述しました。

しかし、これだとprefixのpathが変わってしまうのでサインアップ、ログイン後のpathを変更しないといけません。

なので、変更していきます。

 

 

 

 

⑧パスの変更

app/controllers/application_controller.rb

def after_sign_in_path_for(resource)
 users_user_page_users_path
end

 

app/views/chat_groups/index.html.erb

<div class="chat-group__user-page">
 <%= link_to 'ユーザーページへ', users_user_page_users_path %>
</div>

 

 

 

 

 

 

⑨ユーザーページの編集及び編集ページを作成

先にユーザーページの編集をします。

https://gyazo.com/701067588f35ea73d4ad0753ed18f460

編集ページへのパスは/users/:id/editなのでlink_toメソッドを使用し、パスは"/users/#{@user_info.id}/edit"としました。

app/views/users/edit.html.erbを作成。

https://gyazo.com/80380b0b39a93fa5687765ac2b5b285a

updateへのpathは/users/:idなのでurl: "/users/#{@edit_user.id}"を設定。

また、form_withのデフォルトHTTP動詞はpostなのでmethod: :patchを設定。

ユーザーページから『アカウント設定』をクリック。

編集してみます。

https://gyazo.com/662fd586bfac5a928a26f2079b1c0bba

大丈夫そう。

 

 

 

 

 

 

⑩ユーザーページのアイコンを表示

ここで注意しなければならないのはアイコンを登録してないユーザーはデフォルトアイコン画像↓

f:id:No_idea:20200703095316p:plain

でなければならないことです。

なので最初にデバッグをしながら処理内容を決めていきたいと思います。

user_page.html.erb

<%= debug @user_info.user_icon.file%>

分かりやすいように一番下でdebugをしてみます。

@user_infoにはユーザー情報が含まれています。

その中でuser_iconのfileはどうかをアイコン登録者と未登録者で比較してみます。

アイコン登録者:

未登録者:

登録してあるユーザーではfile:のところにどこにアイコン画像が保存されているか書かれています。

しかし、未登録者では...となっていて何も書いていません(nil)。

これを使ってif文で分岐させて処理させれば良さそうです。

app/controllers/users_controller.rb

def user_page
 @user_info = User.find(current_user.id)

 if @user_info.user_icon.file != nil
  @user_icon = @user_info.user_icon.url
 else
  @user_icon = 'user_icon_default.png'
 end
end

fileがある、つまり登録者は@user_iconへアイコンのurl(どこへ保存されているか)の情報を代入。

未登録者では@user_iconへデフォルトアイコン画像を代入しています。

次にビューを編集していきます。

app/views/users/user_page.html.erb

image_tagに@user_iconとコードすることでアイコンのurlを代入します。

http://localhost:3000/users/users/user_page

登録あり:

 

登録なし:

ユーザーページでのユーザーアイコンの表示は問題なさそうです。

 

 

 

 

 

 

①①チャットグループへアイコンを表示

現在のチャットグループは↓のような表示になっています。

なのでチャットグループへアイコンを表示してみたいと思います。

イメージ図:

まだチャットグループへのメンバー登録機能は実装していないので、アイコンはグループ作成者のみアイコンが表示されるようにしたいと思います。

ここでも注意することがあります。先ほどと同様にアイコンを登録していない人はデフォルトアイコンを表示させるようにしなければなりません。

また、チャットグループのアイコンも同じで登録していない場合はデフォルトの画像を表示しなければなりません。

今回使用するチャットグループデフォルトアイコンは↓のものにしようと思います。

f:id:No_idea:20200703105918p:plain
それではやっていきたいと思います。

 

最初にチャットグループのアイコンが表示できるようにします。

app/controllers/chat_groups_controller.rb

def show
 @chat_group = ChatGroup.find(params[:id])
 
 if @chat_group.chat_group_icon.file != nil
  @chat_group_icon = @chat_group.chat_group_icon.url
 else
  @chat_group_icon = 'group_icon_default.png'
 end

end

ユーザーのアイコン表示と同じようにアイコンのファイルが何もない(nil)場合= 画像がある場合は@chat_group_iconにアイコンのurlを代入。

なければ@chat_group_iconにデフォルトアイコン画像を代入。

app/views/chat_groups/show.html.erb

 

登録あり:

 

登録なし:

アイコンを表示することに成功しました。

次にユーザーアイコンを表示させます。

app/controllres/chat_groups_controller.rb


def show
@chat_group = ChatGroup.find(params[:id])
 
    .
    . (省略)
    .
@chat_members = ChatGroupToUser.where(chat_group_id: @chat_group.id)
@chat_member_info = User.find(@chat_members.ids)
end

@chat_group

ここではchat_groupsテーブルから表示する情報をidから引っ張り出してきます。

これでレコード(横{行})が取得できました。

@chat_members

ここではwhereメソッドを使用し、chat_group_idカラムの中で@chat_group.id(表示したいページのid)を中間テーブル(chat_groupとuser)から探してきます。

これで同じチャットに参加しているメンバーのidも取得できるようになりました。

@chat_members_info

先ほどの@chat_membersを使用し、参加メンバーの情報を取得します。

@chat_members.idではなく@chat_members.idsで複数のユーザーの情報を取得できます。

app/views/chat_groups/show.html.erb

https://gyazo.com/92804441949ae74f811590bf8065ab64

ユーザーページのアイコンやチャットグループのアイコンのように1つだけの画像を表示ならばコントローラーへコードしインスタンス変数を持ってくれば大丈夫なのですが、2つ以上のアイコンを表示する場合、つまり情報を一つ一つ取り出す際はhtmlの方で分岐をコードしなければいけないみたいです。

each do |〇〇|で一つずつユーザー情報を取り出す処理をしています。

今は作成者のみですが、今後メンバー追加するときにこれでメンバーのアイコンを表示することができます。

コードできたので表示されるかやってみます。

登録あり:

登録なし:

できてます。

最後にアイコンを丸くし整えます。

 

 

 

 

①②各アイコンを整える

チャットグループでのアイコン(グループアイコン、ユーザーアイコン)を丸く表示したいので、今回は@mixinと@includeを使用してスタイル(今回は大きさと丸くするスタイル)を使い回したいと思います。

@mixinと@includeはセットで使用します。

これを使うと共通のスタイルが多い場合とかにコードが楽になります。

また、コードも見やすくなり一石二鳥です✌️

参照:https://stand-4u.com/css/sass/mixin.html

 

 

app/assets/stylesheets/chat_groupsフォルダを作成。

app/assets/stylesheets/mixinフォルダを作成。

app/assets/stylesheets/chat_groups/show.scssファイルを作成。

app/assets/stylesheets/mixin/_icon_setting.scssファイルを作成。

app/assets/stylesheets/application.scss

@import "mixin/icon_setting";
@import "users/user_page";
@import "chat_groups/show";

これで使えるscssが使用できるようになりました。

app/assets/stylesheets/chat_groups/show.scss

https://gyazo.com/5755c84bb5086403be8e4b4ad88b5297

大きさと丸みの設定してみました。

これでよかったら @mixinと@includeを使用して書いていきたいと思います。

app/assets/stylesheets/mixin/_icon_setting.scss

@mixin chat_icon {
 height: 50px;
 width: 50px;
 border-radius: 50%;
}

@mixin (任意の付けたい引数名){

  (処理内容)

}

 

app/assets/stylesheets/chat_groups/show.scss

https://gyazo.com/834e69f01ecf19a5e914d3966edc624a

@inculde (@mixinで付けた引数名);

これで@mixinから@inculdeにデータが引き継がれ使えるようになります。

では、ビューを確認してみます。

完成しました。

 

 

 

 

 

 

 

次回はチャットメンバーを追加できるように実装していきたいと思います。

その際に、インクリメンタルサーチ(入力した文字に該当する候補を出してくれる機能)と非同期通信(ページを遷移せずに一部だけを更新できる機能)を使用し追加できるようにします。

この2つスクールで学習してましたが、とりあえず完成させて提出したって感じで理解できないままやってましたね^^;

そのせいで実装に苦労しました。

このブログでもう一度実装し、記憶を定着させられるようにしたいと思います。

 

インクリメンタルサーチhttps://gyazo.com/f0f6a5b5ed8ebf2d6477373d0da548f9

 

 

非同期通信:https://gyazo.com/6644ad0a2b4eca5249326856dbb20ce7

 

 

 

 

 

 

 

以上です。

Ruby on Rails: チャットグループの詳細ページ、編集、削除機能の実装...chapter14

今日やりたいこと

・チャットグループの詳細ページの作成

・チャットグループ編集機能の実装

・チャットグループ削除機能の実装

 

この3つをやろうと思います。

最初にチャットグループの詳細ページを作成します。

 

①チャットグループの詳細ページの作成

1.コントローラーの編集

app/controllers/chat_groups_controller.rb

def show
 @chat_group = ChatGroup.find(params[:id])
end

ChatGroupモデルを使用し、params[:id]を使用しchat_groupsテーブルからidを探してチャットグループ情報を取得。

@を変数につけてインスタンス変数にして@chat_groupに取得したチャットグループ情報を代入。

 

2.詳細ページの作成

app/views/chat_groups/show.html.erbを作成。

https://gyazo.com/08889858d742773eb9528663d6bd8e20

チャットグループの説明

<% if @chat_group.chat_group_description != "" %>

もしchat_groupsテーブルのchat_group_description(グループの説明)カラムが空欄なら。。。

<div class="chat-group-show__description">
 <span class="chat-group-show__description--content">説明:</span>
 <span class="chat-group-show__description--des">
  <%= @chat_group.chat_group_description %>
 </span>
</div>

説明を表示してください。

<% else %>

それ以外なら(説明文がない場合)。。。

<div class="chat-group-show__none">
説明文はありません。
</div>

『説明文はありません。』と表示してください。

<% end %>

if文終了してください。

 

3.詳細ページの確認

/説明文がない場合

//説明文がある場合

 

 

②チャットグループ編集機能の実装

現在一覧ページは↑のようになっています。

メンバーカウント数の横に編集リンクを作りたいと思います。

そして、編集フォームに保存されているグループ名と説明をフォームに表示させるようにしたいと思います。

 

1.編集リンクを作成

一覧ページに編集リンクをつけていきます。

$rails routes

  edit_chat_group GET    /chat_groups/:id/edit(.:format)                                                          chat_groups#edit

app/views/index.html.erb

http://localhost:3000/chat_groups

 

 

2.コントローラーの編集

app/controllers/chat_groups_controller.rb

def edit
 @edit_chat_group = ChatGroup.find(params[:id])
end
 
def update
 update_chat_group = ChatGroup.find(params[:id])
 update_chat_group.update(chat_group_params)
end
 
private
def chat_group_params
 params.require(:chat_group).permit(:chat_group_name, :chat_group_description, user_ids:[])
end

editアクション

先ほどの詳細ページ(showメソッド)と同じ処理。

今回はインスタンス変数を@edit_chat_groupとしました。

 

updateアクション

チャットグループを探し、変数update_chat_groupへ情報を代入。

編集して送られてきた情報をストロングパラメーター(chat_group_paramsメソッド内)にかけている情報だけを通す。

そしてupdateメソッドを使用し、内容を更新してDBへ保存。

 

3.編集ページの作成

app/views/chat_groups/edit.html.erbを作成

https://gyazo.com/07ac61e8b45b19496f41dcaabefefa48

form_withのデフォルトHTTP動詞は'post'なので、methodでpatchを指定。

url

PATCH  /chat_groups/:id(.:format)    chat_groups#update

$rails routesで確認。

url: '/chat_groups/#{@edit_chat_group.id}'

これでupdateアクションへ行けそうです。

チャットグループ一覧ページから[編集]をクリック。

これを編集してみます。

これで編集するボタンをクリックし、一覧ページへ戻ってみると。。。

 

詳細ページ:

大丈夫そうです。

 

 

 

 

③チャットグループ削除機能の実装

最後にチャットグループを削除できるようにしていきます。

 

1.コントローラーを編集

def destroy
 delete_chat_group = ChatGroup.find(params[:id])
 delete_chat_group.destroy
end

チャットグループを探して、destroyメソッドで削除を指示。

2.削除リンクを貼る

 

編集の横に削除パスを貼ります。

 

$rails routes

DELETE /chat_groups/:id(.:format)        chat_groups#destroy

パス先は"/chat_group/#{list.id}"。

3.ビューの編集

app/views/chat_groups/index.html.erb

link_toのデフォルトHTTP動詞は'get'なので methodを使用して'delete'と指定。

4.確認

http://localhost:3000/chat_groups

削除できるかやってみます。

グループ3を削除してみます。

[削除]をクリック

http://localhost:3000/chat_groups

削除できてます。

 

 

 

 

今回は過去の復習でした。

昔だったら時間をかけて作っていたと思いますが、今回はすらすら作成できました😄

 

 

 

 

 

 

 

 

 

以上です。

Ruby on Rails: チャットグループ一覧ページの作成...chapter13-4

今回やりたいこと

・グループの新規作成機能の実装

・チャットグループ一覧からチャット新規作成ページへ遷移できるようにする

・チャットグループ一覧へ所属するグループ名と参加人数を表示。

 

①chat_groups_controller.rbの編集

$rails routes

https://gyazo.com/4065f05257221c26bb9a47d043aac3ee

app/controllers/chat_groups_controller.rb

def new
 @chat_group_new = ChatGroup.new
end

ChatGroupモデルを使用し、newでインスタンスを生成。

 

 

 

 

 

 

②新規作成ページを作成(仮) + チャットグループ一覧ページから新規作成ページへリンク

app/views/chat_groups/new.html.erbを作成

app/views/chat_groups/index.html.erb

(修正前)
<div class="chat-group__new">
<%= link_to 'グループ新規作成', chat_groups_path %>
</div>
(修正後)
<div class="chat-group__new">
 <%= link_to 'グループ新規作成', new_chat_group_path %>
</div>

リロード🔁

http://localhost:3000/chat_groups

グループ新規作成クリック

これで新規作成ページに遷移できるようになりました。

 

 

 

 

 

 

 

③新規作成ページを作成

app/views/chat_groups/new.html.erb

form_withを使用しcreateアクションのパスへ飛ばしたいので、パスはchat_groups_path。

さらにmodelを使用し、どこのテーブルに情報を送りたいのかを示します。

リロード🔁

フォームは出来ました。

次にこのフォームで入力した情報を保存できるようにcreateアクションを作成し、保存できるようにします。

 

 

 

 

 

④コントローラーにcreateアクションを追加

def create
 chat_group_new = ChatGroup.new(chat_group_params)
 if chat_group_new.save
  redirect_to action: 'index', notice: 'チャットグループを作成しました'
 else
  render :new, alert: 'チャットグループ作成に失敗しました'
 end
end

private
def chat_group_params
 params.require(:chat_group).permit(:chat_group_name, :chat_group_description)
end

createアクションの処理内容

ChatGroup.newでインスタンス生成をして、変数chat_group_newに代入。

その際に、いらない情報も保存されないようにストロングパラメーターを設定。

もしchat_group_newがセーブできたら、簡易メッセージで'チャットグループを作成しました'と表示し、リダイレクト(遷移先)にindexアクションを設定。

情報をセーブできない場合はアラートとして'チャットグループ作成に失敗しました'と表示。

 

 

 

 

⑤情報を保存できるか確認

$rails s

新規作成してみると。。。

新規作成ボタンをクリックした瞬間にindexページへ遷移する。

アドレスバーにチャットグループを作成しましたと簡易メッセージが表示される。

Sequel Proでもみてみる。

保存できている。

 

しかしチャットグループを作成した場合、”チャットグループリスト表示”されないとおかしいです。

def index
 @chat_group_lists = ChatGroup.all
 @chat_group_joining = ChatGroupToUser.where(user_id: current_user.id)
 @chat_group_lists_none = "チャットグループに参加していません"
end

理由としてはこのチャットグループ一覧ページで設定したif @chat_group_joining == にあります。

これはindexアクションでの処理内容ですが、@chat_group_joining = ChatGroupToUser.where(user_id: current_user.id)でChatGroupUserToUserモデルを使用し、chat_group_usersテーブル(中間テーブル)からuser_idカラムからログインしているユーザーを探してきてと命令しています。

なのでchat_group_usersテーブルにログインしているユーザーがないと”チャットグループリスト表示”は表示されません。

 

chat_group_usersテーブルをみてみます。

やはり、保存されていません。

なので保存されるように設定していきたいと思います。

 

 

 

 

 

 

⑥保存できるように修正

app/views/chat_groups/new.html.erb

<h1>チャット新規作成</h1>
 
<%= form_with url: chat_groups_path, model: @chat_group_new do |f| %>
<%= text_field_tag 'chat_group[user_ids]', "#{current_user.id}", type: 'hidden' %>
<div class="chat-group-new__forms">
<div class="chat-group-new__forms__name">
<%= f.label :chat_group_name, 'グループ名', class: "chat-group-new__forms__name--label" %>
<%= f.text_field :chat_group_name, class: "chat-group-new__forms__name--field" %>
</div>
        ・
        ・ (省略)
        ・

text_field_tag 'chat_group[user_ids][], "#{current_user.id}", type: hidden'

text_field_tagはフォームを作成し、送りたい情報を送れるようにするメソッドです。

構成は以下のようになっています。

text_field_tag (テキストボックス名, 初期文字列, {オプション1, オプション2,,,})

 

テキストボックス名

テキストフィールドのidとnameに割り当てられる。

フォームからの値を取得するキーになる。

 

初期文字列

テキストフィールドのデフォルト値。

指定された文字列が入力された状態で表示される。

 

オプション

今回はtype: hiddenを設定。

これで情報は送れるが、フォームはなくなります。

type: hidden設定なし

 

type: hidden設定あり

これで送られている情報は↓のようになっている。

<input type="hidden" name="chat_group[user_ids][]" id="chat_group_user_ids_" value="5"
  />

これで一旦グループを作成してみる。

保存はできているが、「参加していません」になっている。

理由は

https://gyazo.com/1c55a541b0afaa610748aa26bcad6f59

ストロングパラメーターにuser_idsを受け取れるようにしていない。

とりあえず、ストロングパラメーターにuser_idsを設定してみる。

:user_idsとしたのは

Parameters: {"authenticity_token"=>"daUxVjXs0ZCF6gsdjnb1ZLDTOUJExuUKKpOtOEPB973R+V5o54jNR8P0Rkb+rfkXqhO24w+2Vt1WmvzmBY9Jvw==", "chat_group"=>{"user_ids"=>["5"], "chat_group_name"=>"グループ2", "chat_group_description"=>"グループ2"}, "commit"=>"新規作成"}

Unpermitted parameter: :user_ids

user_idsを受け取るときに配列となっている。なので、配列で受け取れるようにを設定。

これでリロードし、作成してみる。

リロード🔁

新規作成してみる。

遷移できてない。

https://gyazo.com/afeee14471c243f133441bd768bd7f42

今度はエラー内容をみてみると、

ArgumentError (wrong number of arguments (given 0, expected 1..2)):

ArgumentError(引数の数が間違っている(0の場合、1..2が必要)):

と書いてある。

また、

app/controllers/chat_groups_controller.rb:26:in `'

app/controllers/chat_groups_controller.rb:26:in `chat_group_params'

app/controllers/chat_groups_controller.rb:15:in `create'

これはこの書いてあるところが怪しいと言っています。

:user_ids→user_ids:へ変更。

リロード🔁

新規作成してみる。

https://gyazo.com/800f1e7c3dd376377105ef8c94d4aaa6

ActiveModel::UnknownAttributeError (unknown attribute 'user_ids' for ChatGroup.):

このエラーを解決していきます。

$rails c

pry> show-models

ChatGroupの

has_many :chat_group (through :chat_group_to_users)

ここが間違ってました。ここは中間テーブルのchat_group_to_usersを経由してusersテーブルと紐付けると示す場所なのにchat_groupテーブル(しかもhas_manyなのに複数形にもしてない...)に紐づけてしまっています。

なので、has_many :users (through :chat_group_to_users)と変更。

これでトライしてみます。

$rails s

チャットグループリスト表示になりました!

Sequel Proも開いて確認。

中間テーブルにも保存されています!

 

それでは次はチャットグループ一覧ページに所属するチャットグループと所属人数が

 

 

 

 

 

 

⑦グループ一覧ページに所属するチャットグループ名と所属人数を表示する。

https://gyazo.com/bac3874bccce832c6de2605713c982bf

 

<% else %>~<% end %>を変更。

@chat_group_list.each do |list| ~ end

ここで、chat_groupsテーブルに入っている情報(今回はグループ1〜3まで)を繰り返し、一つ一つ取り出してます。

 

if list.user_ids.include?(current_user.id) ~ end

そのDBに保存されているuser_id達の中にcurrent_user(ログインしている)ユーザーは含まれているかを聞いてます。

 

link_to "/chat_groups/#{list.id}" do ~ end

まだグループの詳細ページは作成していませんが、詳細ページへ飛べるように式展開を使用しリンクを貼りました。

 

list.chat_group_name

ここで、所属しているチャットグループ名を表示。

 

list.user_ids.count

所属しているuser_id達をカウント(countメソッド)してくださいと命令。

 

 

これでリロードしてみてみます。

リロード🔁

できてます。

さらにグループ4も登録してみてみます。

問題なさそうです。

 

 

 

今回はチャットグループを作成し、一覧ページに表示できるようにしました。

エラーがたくさん出て苦労しましたが、エラー構文の勉強になるので良かったかも(?)

次回はチャットグループ詳細ページ、編集、削除を実装していきたいと思います。

 

 

 

 

 

 

 

 

 

以上です。