某スクールにおいて、チーム開発で、フリーマーケットアプリを作成中であり、使用した技術について公開しています。
※初学者のため、ミスや認識違いが多々あると思いますがご了承ください。
商品詳細ページにコメント機能を実装しました。
全部で7回に分けて記事を投稿しています。
|内容 | url |
|:-----------------|------------------:|:-----------------------------:|
| 第1回 | モデル、マイグレーション編 | https://qiita.com/sho_U/items/03108801146e65d58413 |
| 第2回 | ルーティング編 | https://qiita.com/sho_U/items/5c829b3060be2cce919a |
| 第3回|コントローラー編|https://qiita.com/sho_U/items/8528f336cf0d470cd719|
|第4.1回|ヴュー編(一覧表示)|https://qiita.com/sho_U/items/6190562270c722956547|
|第4.2回|ヴュー編(インプットフォーム)|https://qiita.com/sho_U/items/67c2ede4fc6c605283e2|
|第5回|jquery編|https://qiita.com/sho_U/items/d72b60114f76380d05f6|
|第6回|ajax編|https://qiita.com/sho_U/items/caed9b1471e63d43dd3a|
〜コード全文〜https://qiita.com/sho_U/items/310ac5b653bdfcb99a2c
#ajax(非同期通信について)
ajaxを用いて実装する部分は、**「コメント作成」「コメント復元」**した場合となります。「コメント作成」「コメント復元」については、非同期通信を用いてデーターベースにアクセスし、受け取ったデーターを元に、画面上の表示の一部を作り替えて変更します。
##まずは**「コメント作成」**について説明したします。
1. 「コメントする」をクリックしたら、jqueryが発火
2. 入力されたデーターを非同期でサーバーに送り、json情報を受け取る。
3. 受け取った情報を元に条件をif文でふるいにかけてhtmlを形成する。
4. 作成作成したhtmlをコメント一覧に追加する。
という流れになります。
以下が、コメントを追加するボタンをクリックした際に、発動するプログラムとなります。
// ===================================
// コメント作成した場合
// ===================================
$('.new_comment').on('submit', function(e){
e.preventDefault()
var formData = new FormData(this);
var url = $(this).attr('action');
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
.done(function(comment_data){
var html = new_comment(comment_data);
$(".comment_list").append(html)
$('#comment_body').val("");
$('.comment_list').animate({ scrollTop: $('.comment_list')[0].scrollHeight});
})
.fail(function() {
alert("メッセージ送信に失敗しました");
});
});
解説します
$('.new_comment').on('submit', function(e){
e.preventDefault()
onメソッドで、第一引数にsubmitイベントを指定し、フォームデーターが送信されたら発火して、まずブラウザのデフォルトの動作を停止します。
var formData = new FormData(this);
イベント発火元の情報**("this")を引数にFromDataを作成し、変数formDataに格納します。
ちなみにFormDataとは、読み込んだブラウザ上から、さらにHttp通信リクエストを行うためのAPIであるXMLHttpRequest**を使用して、送信するためのキーと値のペアのセットを収集可能にします。
FormDataについて(参考)
https://qiita.com/akkt222/items/ce93d53e8d92a78a0694
XMLHttpRequestについて(参考)
https://ja.javascript.info/xmlhttprequest
XMLについて(参考)
https://www.cybertech.co.jp/xml/contents/xmlxmldb/serial/_xmlbeginner2.php
var url = $(this).attr('action');
attrメソッドで、発火元フォームのaction属性の値("/comments")を取得し変数urlに格納しています。
ちなみにフォームタグの属性は以下のようになっています。
<form class="new_comment" action="/comments" accept-charset="UTF-8" method="post">
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
ajax通信を行います。
url: は、先ほどの変数urlに入っている値、つまり "/comments" 。
type: でHTTPのリクエストメソッドをPOSTに指定。これにより、commentsコントローラーのcreateアクションを送信先に指定します。
dataType: で受け取るデーターのタイプを jsonに指定します。
processData: false
contentType: false
data:
サーバーへ送信するデータです。 もし文字列で無ければ、クエリー文字列に変換されます。 これはGETリクエストのURLに追加されます。 この自動的に行われるプロセスを防ぎたい場合は、processDataのオプションを参照してください。
processData:
デフォルトでは、dataオプションにオブジェクトとして渡されるデータ(厳密に言えば、文字列以外のもの)は、 デフォルトのcontent-typeである"application/x-www-form-urlencoded"に合わせた形式でクエリー文字列へ変換されます。 もしDOMDocument、またはその他の形式のデータを送信したい場合は、このオプションをfalseに設定します。
参考:https://nodoame.net/archives/7215
contentType:はデータ送信時のcontent-typeヘッダの値になりますが、FormDataオブジェクトの場合は適切なcontentTypeが設定されるので、同じくfalseを設定します。
参考:http://www.koikikukan.com/archives/2014/09/30-013333.php
今回は、httpメソッドは”post"であり、クエリー文字列に変換する必要はないためfalseとなる。
####続いては、データーが送られてきたcommentsコントローラーのcreateアクションでの処理をみていきます。
def create
@comment = Comment.new(comment_params)
@seller_of_item = @comment.item.seller
if @comment.save
respond_to do |format|
format.json
end
else
flash[:alert] = "保存できていません"
redirect_to item_path(params[:id])
end
end
private
def comment_params
params.require(:comment).permit(:comment,:item_id).merge(user_id: current_user.id)
end
end
解説します。
@commnetsに、フォームから送られてきたデーターを、@seller_of_itemには出品者を格納しています。
(詳細は、第3回〜コントローラー編〜https://qiita.com/sho_U/items/8528f336cf0d470cd719)
if @comment.save
respond_to do |format|
format.json
end
@commnetのsaveが正常に行われた場合、respond_toメソッドにより、json形式でリクエストされた場合は、jbuilderを通じて、json形式でデーターが返されます。
json.comment @comment.comment
json.id @comment.id
json.user_nickname @comment.user.nickname
json.user_id @comment.user.id
json.created_at @comment.created_at.strftime("%Y年%m月%d日 %H時%M分")
json.item_seller @seller_of_item
jbuilderは
json.<キー> <値>
=> {"キー": "値"}
とすることで簡単に、json形式でデーターを作成することができます。
.done(function(comment_data){
var html = new_comment(comment_data);
通信が成功した場合は、返されたjsonを、comment_dataとして引数に格納します。
検証で、comment_dataの中身を確認すると
comment_data:
comment: "お安く提供します。"
created_at: "2020年05月02日 00時52分"
id: 21
item_seller:
avatar: null
birthday: "2018-06-06"
created_at: "2020-04-04T14:55:54.000+09:00"
email: "2@gmail.com"
family_name: "高木"
family_name_reading: "タカギ"
first_name: "ぶー"
first_name_reading: "ブー"
id: 2
nickname: "ブー"
password: null
phone_number: "08022222222"
self_introduction: null
updated_at: "2020-04-04T14:55:54.000+09:00"
__proto__: Object
user_id: 2
user_nickname: "ブー"
json形式でコントローラーから送られたデーターが格納されていることがわかります。
このcomment_dataを引数に、new_commentメソッドを呼び出しています。
呼び出した返り値を変数htmlに格納します。
// ===================================
// 新規コメント表示用・自分のコメント復元用
// ===================================
function new_comment(comment_data){
var HTML_content_time =
`
<div class="comment_Me comment_one_block" data-index=${comment_data.id}>
<div class="comment_content">
${comment_data.comment}
<div class="comment_create_at">
${comment_data.created_at}
</div>
`
var HTML_deleteBtn =
`
<div class="comment_delete me_pre_delete" data-index=${comment_data.id}>
<a rel="nofollow" data-method="patch" href="/comments/${comment_data.id}">削除する</a>
</div>
`
var HTML_nickname =
`
</div>
<div class="comment_user_name">
${comment_data.user_nickname}
`
var HTML_sellerMark =
`
<div class="seller_display">
出品者
</div>
`
var HTML_endDiv =
`
</div>
</div>
`
if (comment_data.item_seller.id == comment_data.user_id){
// 出品者とコメントしたユーザーが等しい場合
var html = HTML_content_time + HTML_deleteBtn + HTML_nickname + HTML_sellerMark + HTML_endDiv
}else{
// 出品者とコメントしたユーザーが異なる場合
var html = HTML_content_time + HTML_nickname + HTML_endDiv
};
return html;
};
追加するコメントを、部分ごとに分けて作成し、出品者がコメントしたのか、出品者以外がコメントしたのかでif文で場合分けして、変数htmlを組み立てています。(出品者のコメントには、削除ボタンと出品者マークが表示される)
$(".comment-list").append(html)
$('#comment_body').val("");
$('.comment_list').animate({ scrollTop: $('.comment_list')[0].scrollHeight});
帰ってきた変数htmlをコメントの一覧に追加して、コメントフォームの中身(入力された文字)を消去して、画面を新しく追加したコメントの位置までスクロールします。
これで、非同期通信を用いた**「コメント作成」機能**が実装できました!!
##最後に**「コメント復元」**について説明したします。
以下が、「復元するボタン」をクリックした際に、発動するプログラムとなります。
// ===================================
// 復元した場合
// ===================================
$(".comment-list").on('click',".comment-restore",function(e){
e.preventDefault()
var index = $(this).data("index")
var url =`/comments/${index}/restore`
$.ajax({
url: url,
type: "get",
dataType: 'json',
})
.done(function(comment_data){
if (comment_data.item_seller.id == comment_data.user_id){ // 出品者とコメントユーザーが同じ場合
var html = new_comment(comment_data);
$(`.comment-one-block[data-index=${index}]`).replaceWith(html)
}else{ // 出品者とコメントユーザーが異なる場合
var html = restore_other_comment(comment_data);
$(`.comment-one-block[data-index=${index}]`).replaceWith(html)
}
})
.fail(function() {
alert("メッセージ送信に失敗しました");
});
});
解説します。
$(".comment-list").on('click',".comment-restore",function(e){
e.preventDefault()
var index = $(this).data("index")
var url =`/comments/${index}/restore`
「復元ボタン」がクリックされたら発火します。
発火元の「復元ボタン」に与えられたカスタム属性値(コメントのid)が、変数indexに格納されます。
変数urlには、pathの中にindexが埋め込まれた形で格納されます。
$.ajax({
url: url,
type: "get",
dataType: 'json',
})
commentsコントローラーのupdateアクションに返却値をjson形式でリクエストし、データーが送られます。
def restore
@comment.update(delete_check:0)
@seller_of_item = @comment.item.seller
respond_to do |format|
format.json
end
def set_comment
@comment = Comment.find(params[:id])
end
送られてきたparams[:id]で、復元すべきコメントを特定し、@commnetに格納し、**delete_checkを"0"にして「仮削除状態」**を解除して更新する。
jbuilderを呼び出し、json形式にして返却する。
json.comment @comment.comment
json.id @comment.id
json.user_nickname @comment.user.nickname
json.user_id @comment.user.id
json.created_at @comment.created_at.strftime("%Y年%m月%d日 %H時%M分")
json.item_seller @seller_of_item
.done(function(comment_data){
if (comment_data.item_seller.id == comment_data.user_id){ // 出品者とコメントユーザーが同じ場合
var html = new_comment(comment_data);
$(`.comment-one-block[data-index=${index}]`).replaceWith(html)
}else{ // 出品者とコメントユーザーが異なる場合
var html = restore_other_comment(comment_data);
$(`.comment-one-block[data-index=${index}]`).replaceWith(html)
}
**「出品者自身のコメント」と、「その他のユーザーのコメント」**で場合分けしている。理由は、出品者自身のコメントを復元した場合は、コメント作成時に追加するhtmlと構造が同じであるため、同じコードを流用できるため。
if (comment_data.item_seller.id == comment_data.user_id){ // 出品者とコメントユーザーが同じ場合
var html = new_comment(comment_data); //コメント作成時と同じメソッドを呼び出している。
$(`.comment-one-block[data-index=${index}]`).replaceWith(html)
「復元ボタン」と同じカスタムデーター属性値を持つコメントを取得し(つまり復元ボタンが所属するコメント)、中身をreplaceWithメソッドで返り値のhtmlで置き換えている。
}else{ // 出品者とコメントユーザーが異なる場合
var html = restore_other_comment(comment_data);
$(`.comment_one_block[data-index=${index}]`).replaceWith(html)
}
// ===================================
// 他人のコメント復元用
// ===================================
function restore_other_comment(comment_data){
var html =
`
<div class="comment_Other comment_one_block" data-index=${comment_data.id}>
<div class="comment_user_name">
${comment_data.user_nickname}
</div>
<div class="comment_content_other">
${comment_data.comment}
<div class="comment_create_at">
${comment_data.created_at}
</div>
<div class="comment_delete other_pre_delete" data-index=${comment_data.id}>
<a rel="nofollow" data-method="patch" href="/comments/${comment_data.id}">削除する</a>
</div>
</div>
</div>
`
return html;
};
##これで、コメント機能における全ての実装が完了しました!!