はじめに
こちらの講座でチャットアプリを作成したので、追加のお勉強としてメッセージの削除機能を追加してみました。自分の備忘録を兼ねてやったことをメモ的に書いておきます。
環境
OS:MacOS 14.1.1(Sonoma)
Ruby:2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin23]
Vue.js:@vue/cli 5.0.8
仕様
- メッセージごとに削除ボタンを表示し、クリックすると該当メッセージが削除される
- 削除対象はチャットにログインしている本人が送信したメッセージのみ
- 他人のメッセージには削除ボタンを表示させない
- メッセージ削除に成功するとメッセージ一覧が再取得され、削除したメッセージは画面から消える
ルートを設定する
- メッセージ削除のルートを追加します
resources :messages, only: ['index', 'destroy']
元々はメッセージ一覧取得用のindexだけ設定していたので今回はメッセージ削除のためにdestroyを追加しました。resources :messages
だけだとCRUD全てのルートが作成されます。
メッセージDELETEのルートが設定されていることを確認する
$ rails routes | grep message
message DELETE /messages/:id(.:format) messages#destroy {:format=>:json}
messagesにDELETEのルーティングが追加されていることがわかります
メッセージ削除のメソッドを実装する
- メッセージのコントローラに以下の通りコードを追加します
before_action :authenticate_user!, only: [ "destroy"]
// ...省略
def destroy
message = Message.find(params[:id])
if (request.headers[:uid] == message.user.uid)
if message.destroy
render json: {id: message.id, email: message.user.email, message: 'メッセージの削除に成功しました'}, status: :ok
else
render json: {message: 'メッセージの削除に失敗しました', errors: message.errors.messages}, status: :badrequest
end
else
render json: {message: 'メッセージ作成者が違います'}, status: 401
end
end
説明
before_action :authenticate_user!, only: [ "destroy"]
ここで削除の実行前に必ずユーザーがログイン済みであることをチェックしています
- authenticate_user!とは?
- deviseのメソッド
- これをコントローラの先頭に書くことでユーザがログインしているかどうかを確認し、ログインユーザーのみ処理を実行できるようにする
message = Message.find(params[:id])
メッセージのidをパラメタで渡すようにするので、パラメタのIDをもとにメッセージのレコードを検索し、messageという変数に格納しておきます。
if (request.headers[:uid] == message.user.uid)
// ...省略
else
render json: {message: 'メッセージ作成者が違います'}, status: 401
end
削除条件の削除実行者とメッセージ作成者が一致することを確認しておきます。
ここで条件に一致しない場合(=メッセージ作成者と削除リクエスト元のユーザーが一致しない場合)は、HTTPレスポンスとして401エラーを返すようにします。
if message.destroy
render json: {id: message.id, email: message.user.email, message: 'メッセージの削除に成功しました'}, status: :ok
メッセージの削除に成功した場合、成功のHTTPレスポンス(ok)を返します。200でも良いですがわかりやすくokにしておきます。
render json: {message: 'メッセージの削除に失敗しました', errors: message.errors.messages}, status: :badrequest
メッセージ削除に失敗した場合、Bad Requestというステータスとエラー内容を返します。
PostmanでAPIの動作確認をする
ログイン情報をヘッダに付与した状態でメソッド:DELETEのAPIを叩いてみます。
http://localhost:3000/messages/[message.id]
以下のようなレスポンスが確認できました。
{"id": 3,"email": "test@gmail.com","message": "メッセージの削除に成功しました"}
Message Destroy (1.6ms) DELETE FROM "messages" WHERE "messages"."id" = ? [["id", 3]]
railsのログでもしっかり消えていることが確認できます
{"message": "メッセージ作成者が違います"}
Completed 401 Unauthorized in 12ms (Views: 0.3ms | ActiveRecord: 0.9ms | Allocations: 4127)
railsのログでもちゃんと401で怒られました。
メッセージ削除ボタンを実装する
<span class="deletebutton" @click="deleteMessage(message.id)">削除</span>
メッセージ一覧として画面に出力しているので、各メッセージに削除ボタンをspanタグで追加します。
クリックしたらdeleteMessageが発火します。その時(message.id)を引数として渡してあげるようにします。
methods: {
async deleteMessage (messageId) {
try {
const res = await axios.delete(`http://localhost:3000/messages/${messageId}`,
{
headers: {
uid: this.uid,
"access-token": window.localStorage.getItem('access-token'),
client: window.localStorage.getItem('client')
}
})
if (!res) {
new Error('メッセージを削除できませんでした')
}
this.$emit('connectCable')
} catch (error) {
console.log(error)
}
},
deleteMessageのメソッドです。ヘッダーとしてログイン情報を付与し、DELETEのAPIを叩きます。
CSSの設定
.received .deletebutton {display: none;}
自分のメッセージか否かを見分けるようにしているので、受信した他人のメッセージは削除ボタン非表示にします。
.sent .deletebutton {
display: inline;
background: #eee;
border-radius: 30px;
padding: 5px;
color: #999;
font-size: 10px;
margin-bottom: 20px;
margin-left: 4px;
margin-right: 4px;
}
自分が送信したメッセージには削除ボタンを表示させます。
本当はメッセージの要素の下、送信時間の右横にボタンを表示させたかったのですが、うまくいかずとりあえずdisplay: inline;でメッセージ要素の右横にボタンを表示させてみました...
できた画面
こんな感じ(CSS勉強します)
削除ボタンを押すとメッセージが削除され、メッセージ一覧が再読み込みされます。
これでメッセージの削除ボタンが実装できました。次は確認ダイアログを出すようにしてもいいかもしれません。
感想
こちらの講座の解説がわかりやすく、追加したコードの解説が必ず入るので初心者には大変嬉しい内容でした。UIも綺麗でやる気出るのでプログラミング学習はじめたばかりの方におすすめです!