#対象者
- コメント機能を実装予定の方
- 非同期通信(Rails×Ajax)を実装予定の方
#目的
- 記事へのコメントを非同期で投稿・削除ができるようにする
#実際の手順と実例
###1.前提
- jQueryの読み込みができている
- バージョン:Ruby:2.3.0、Rails:5.2.5です。
- Articleモデル、Userモデル実装済み
###2.モデルの作成とテーブル設定
まずコメントモデルを作成します。
UserとArticleとアソシエーションを組みます。
$ rails g model comment user:references article:references
その後、config/db/schema.rbにマイグレーションファイルが作成されているので、下記を追記していきます。
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.text :comment_content #追記
t.references :user
t.references :article, foreign_key: true
t.timestamps
end
end
end
````
上記を確認して、**rails db:migrate**を実行すると
schemaファイルに下記が作成されます。
```scheme.rb
create_table "comments", force: :cascade do |t|
t.text "comment_content"
t.integer "user_id"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["article_id"], name: "index_comments_on_article_id"
t.index ["user_id"], name: "index_comments_on_user_id"
end
```
その後各モデルを確認します。
```app/model/comment.rb
belongs_to :user
belongs_to :post
validates :comment_content, presence: true
```
Commentがないと投稿できないようにバリデーションを設定しました。
```app/model/user.rb
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
```
```app/model/article.rb
belongs_to :user
has_many :comments, dependent: :destroy
```
※ dependent: :destroyでアソシエーションを組んでるものが削除されたとき、それに合わせて削除されるように設定
User:Comment = 1:N
ユーザー1人に対してたくさんの投稿を持てる
Article:Comment = 1:N
記事1つに対してたくさんの投稿を持てる
###3.コントローラーとルーティングの設定
ルーティングはネストして設定します
```route.rb
resources :posts do
resources :comments
end
```
下記コマンドでController作成します。
```
$ rails g controller comment
```
出来上がったControllerに下記を追加していきます。
```app/comments_controller.rb
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.build(comment_params) #①
@comment.user_id = current_user.id
if @comment.save
redirect_to request.referer #②
else
redirect_to request.referer
end
end
def destroy
@comment = Comment.find(params[:id])
if @comment.destroy
redirect_to request.referer
else
redirect_to request.referer
end
end
private
def comment_params
params.require(:comment).permit(:comment_content, :article_id, :user_id)
end
end
```
① .buildメソッドとは親モデルに属する子モデルのインスタンスを新たに生成したい場合に使うメソッドです。外部キーに値が入った状態でインスタンスが生成できます。
②元のページにリダイレクトしてくれます。
https://qiita.com/nao0725/items/54ed720300370dc25dc8
###4.投稿のControllerとViewの設定
今回はarticleに対してCommentを残すので、
articles_controllerに記載します。
````app/controller/articles_controller.rb
def show
@comment = Comment.new
@comments = @article.comments
end
```
```app/views/articles/show.html.erb
<div id = "comment_area"> #①
<%= render "comments/form", comment: @comment %>
</div>
<div>
<%= render "comments/index", comments: @comments, article: @article %>
</div>
```
①のidをターゲットにして、このdiv内をAjaxで書き換えます。(後述)
また、部分テンプレートを使用して、投稿フォームと投稿一覧を同じページに表示します。その2つのViewを作成して、下記の内容を追記します。
`````app/views/comments/_form.html.erb
<div class="form-group row">
<%= form_with(model: [@article, @comment]) do |f| %>
<%= f.label :comment_content,"コメント", class:"form-label" %>
<%= f.text_area :comment_content %>
<%= button_tag type: "submit", class: "btn btn-default" do %>
<i class="far fa-comment-alt"></i>
<% end %> #①
<% end %>
①ではFontAwesomeを使用したのでこのような表記になっています。
<% @comments.each do |comment| %>
<p><%= comment.comment_content %></p>
<% if comment.user == current_user %>
<p><%= link_to "Destroy", article_comment_path(comment.article_id, comment.id), method: :delete, remote: true %></p>
<% end %>
<% end %>
- ログインしているユーザーだけがCommentを削除できるようにif 文を使ってます。
- remote: trueで非同期通信に対応しています。
###5.非同期通信の設定
今回はcreateとdestroyを非同期設定していきます。
app/views/commentsのフォルダ内に下記2つのjsファイルを作成し、追記します。
$("#comments_area").html("<%= j( render "comments/form", comment: @comment ) %>")
$("textarea").val('')
$("#comments_area").html("<%= j( render "comments/form", comment: @comment ) %>")
$("textarea").val('')
両方同じ内容です。
$("textarea").val('')の記述は、コメント入力後のコメント入力欄を空にしています。
これで非同期のComment実装完了です!!
#参照
####投稿者コメント
ここまで長い記事初めて書きました。
結構たいへんでしたが、自分で書いていてかなり理解を深めることができたのではないかと思います。
####My Profile
プログラミング学習歴3ヶ月目のアカウントです!
プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。
また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。
もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。