RailsでAjaxのエラーメッセージを出す


### 目標
![ezgif.com-video-to-gif (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307713/0a1b54d2-bc3c-3357-79c2-48c2e84e23cf.gif)


RailsでAjaxを用いてバリデーションに引っかかった時、エラーメッセージを出す。

Ajaxの例としてコメント機能で書きます。




### 環境

環境　rails 6.0.2.2


簡略的に、Userモデル（nameカラムとemailカラム）、Topicモデル（contentカラムとuser_idカラム）、Commentモデル（reviewカラムとuser_idとtopic_id）とします

Twitterに例えると、Userがツイート（Topic）し、そこにUserがCommentするといった感じです！

以下モデルの関連付けとバリエーション（Commentのみ）です。

```user.rb
has_many :topics, dependent: :destroy
has_many :comments, dependent: :destroy
```

```topic.rb
belongs_to :user
has_many :comments, dependent: :destroy
```

```comment.rb
belongs_to :user
belongs_to :topic

validates :review, presence: true, length: { maximum: 150 }
```

Commentモデルはreviewカラムが「からの時」と「151文字以上」の時にエラーになります。

### コントローラー

まずコントローラーを書いていきます。

```topics_controller.rb
def show
  @topic = Topic.find[:params]
  @comment = Comment.new
  @comments = @topic.comments
end
```
```comments_controller.rb
def create
    @topic = Topic.find(params[:topic_id])
    @comment = @topic.comments.build(comment_params)
    @comment.user_id = current_user.id
    @comment.save
  end

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
  end
```

コメントはtopics/show.html.erbでできるようにしています。

### ビュー

次はビューです

```topics/show.html.erb
<div id="comments_area">
    <%= render partial: 'comments/index', locals: { comments: @comments } %>
  </div>
  <div id="error_explanation">
  </div>
  <div id="form_area">
  <% if current_user %>
    <%= render partial: 'comments/form', locals: { comment: @comment, topic: @topic } %>
  <% end %>
</div>
```
```comments/create.js.erb
$("#comments_area").html("<%= j(render 'index', { comments: @comment.topic.comments }) %>")
$("#error_explanation").html("<%= j(render 'error', { comment: @comment }) %>")
$("textarea").val("")
```
```_form.html.erb
<%= form_with(model: [topic, comment]) do |f| %>

<% if comment.errors.any? %>
      <div id="error_explanation">
 
        <ul>
          <% comment.errors.full_messages.each do |message| %>
            <li><%= message %></li>
          <% end %>
        </ul>
      </div>
  <% end %>

  <div>
    <%= f.label :review %><span style="color: #f00;">(150文字以内)</span><br>
    <%= f.text_area :review,placeholder: "コメントはこちら" %>
  </div>
  <div class="actions mb-3">
    <%= f.submit "コメントをする", class: "btn btn-x btn-outline-secondary" %>
  </div>
<% end %>
```
```comments/_index.html.erb
<% comments.each do |comment| %>
  <% unless comment.id.nil? %>
    <p><%= link_to "#{comment.user.name}さん",user_path(comment.user.id) %></p>
    <p><%= comment.review %></p>
    <% if comment.user == current_user %>
      <p><%= link_to "コメントを削除する",topic_comment_path(comment.topic_id,comment.id),method: :delete, remote: true %></p><hr size="5px" color="black">
    <% end %>
  <% end %>
<% end %>
```

```comments/_error.html.erb
<% if comment.errors.any? %>
  <div class="error_message alert alert-warning">
    <ul>
      <% comment.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>
```



流れとして
①topics/show.html.erbにアクセスすると、_form.html.erbと_index.html.erbが表示されている。indexはすでに保存されているコメントを表示しているhtml。

②formで「コメントをする」ボタンを押すと、commentコントローラーのcreateメソッドが実行される。この時、form_withはデフォルトでremote: trueになっているので、ビューは「create.js.erb」を探す。
（ちなみにlocal: trueとすればcreate.html.erbを探す）

③create.js.erbの1行目では、idがcomments_areaのところに_index.html.erbを表示させている。
2行目では同じように_error.html.erbを表示させている。ただエラーの方を見ると、if構文になっていて、エラーがなければ表示されない。3行目はformのtextareaを空にするコード。

これでエラーが表示されるようになります。

### その他

一応ルートとコメント削除のビューも書いておきます。

```routes.rb
resources :topics do
    resources :comments, only: [:create, :destroy]
  end
```

```destroy.js.erb
$("#comments_area").html("<%= j(render 'index', { comments: @comment.topic.comments }) %>")
$("textarea").val("")
```

以上です！何かおかしいところなどありましたら、ご指摘下さい！

参考 http://eifukun.hatenadiary.jp/entry/2016/01/20/001011