[RUBY] Asynchronous communication in the interactive comment field

Introduction

I am currently attending a programming school called TECH CAMP. I'm developing a flea market clone app, but decided to implement a comment section. The comment section of the flea market apps looks like a dialogue between the "exhibitor" and "others", so I will focus on the points I devised when making this asynchronous communication.

What you want to achieve

I would like to update the comment field by asynchronous communication so that the appearance of the view branches between the seller and other people (customers). The completed sample is as follows.

★ When the customer posts

3c528fb36058573b4181cbd20e09df22.gif

★ When posted by the seller

81970b7d86e801a61e0b752d6c4322df.gif

procedure

This time, assuming that normal comment posting has been completed, I will focus on the subsequent desynchronization and write the procedure.

--Edit controller create action --Create a new jbuilder file --Create js file

Edit controller create action

There are two points.

The first point is that the instance variables "@item" and "@comment" are created at the time of the create action. @item extracts the record of the commented product, and @comment stores the saved comment record. These instance variables will then be used for jsonization in jbuilder.

The second point is that "format.html" is added in respond_to so that if jQuery is not loaded, it can respond even in HTML format. As will be described later, there are cases where the js file does not load properly when using turbolinks together. Of course, it would be best if it could be managed properly, but I think that there are many people who are developing the team for the first time, so I thought that such insurance would be necessary, so I wrote it.

comments_controller.rb


class CommentsController < ApplicationController
  def create
    @item = Item.find(params[:item_id])
    @comment = Comment.create(comment_params)
    respond_to do |format|
      format.html { redirect_to item_path(params[:item_id]) }
      format.json
    end
  end

~ Destroy action omitted this time ~

  private
  def comment_params
    params.require(:comment).permit(:comment).merge(user_id: current_user.id, item_id: params[:item_id])
  end
end

Create new jbuilder file

Create a jbuilder file. There are also two points here. The first point is "json.comment_user_id" on the 3rd line and "json.item_user_id" on the 7th line. After this, conditional branch the view to be added depending on whether it is a seller or not in the js file. If the user_id in the item table and the user_id in the comment table match, you are the seller.

The second point is "json.comment_id" on the first line and "json.comment_item_id" on the fifth line. This is used in the link \ when deleting a comment. Substitute these in the id part of the URL to be deleted to make it the correct link.

ruby:views/comments/create.json.jbuilder


json.comment_id      @comment.id
json.comment         @comment.comment
json.comment_user_id @comment.user_id
json.name            @comment.user.nickname
json.comment_item_id @comment.item_id
json.item_id         @item.id
json.item_user_id    @item.user_id

Create js file

It will be longer, but I will post the full code of the completed code.

comment.js


$(function(){
  function buildHTML(comment){
    var html = `<div class="mainShow__box__content__top__commentBox__comments__comment">
                  <div class="profile">
                    <div class="profile__name">
                      ${comment.name}
                    </div>
                    <div class="profile__right">
                      <div class="image">
                        <i class="fas fa-user-circle"></i>
                      </div>
                      <div class="seller_or_buyer">
Seller
                      </div>
                    </div>
                  </div>
                  <div class="comment">
                    <div class="comment__text">
                      ${comment.comment}
                    </div>
                    <div class="comment__bottom">
                      <div class="comment__date">
                        <i class="far fa-clock"></i>
today
                      </div>
                      <div class="comment__trash">
                        <a item_id="@item" rel="nofollow" data-method="delete" href="/items/${comment.item_id}/comments/${comment.comment_id}"><i class="fa fa-trash"></i>
                        </a>
                      </div>
                    </div>
                  </div>
                </div>`
    return html;
  }

  function buildHTMLother(comment){
    var html = `<div class="mainShow__box__content__top__commentBox__comments__comment--other">
                  <div class="profile">
                    <div class="profile__right">
                      <div class="image">
                        <i class="far fa-user-circle"></i>
                      </div>
                      <div class="seller_or_buyer">
Customer
                      </div>
                    </div>
                    <div class="profile__name">
                      ${comment.name}
                    </div>
                  </div>
                  <div class="comment">
                    <div class="comment__text">
                      ${comment.comment}
                    </div>
                    <div class="comment__bottom">
                      <div class="date">
                        <i class="far fa-clock"></i>
today
                      </div>
                      <div class="comment__icon">
                        <div class="flag">
                          <i class="far fa-flag"></i>
                        </div>
                        <div class="trash">
                          <a item_id="@item" rel="nofollow" data-method="delete" href="/items/${comment.item_id}/comments/${comment.comment_id}"><i class="fa fa-trash"></i></a>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>`
    return html;
  }

  $('#comment_form').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(data){
      if (data.comment_user_id == data.item_user_id) {
        var html = buildHTML(data);
        $('#comments').append(html);
        $('.text_area').val('');
        $('.comment_btn').prop('disabled', false);
      } else {
        var html = buildHTMLother(data);
        $('#comments').append(html);
        $('.text_area').val('');
        $('.comment_btn').prop('disabled', false);
      }
    })
    .fail(function(){
      alert('error');
    })
  })
});

In the first half, the HTML of the view to be added is described. This time, the view is different between the seller and other people, and the CSS is also slightly different, so we have prepared two types. If it is a seller's comment, it will be added by the buildHTML method, and if it is not, it will be added by the buildHTMLother method. In the HTML, in addition to the comment body and nickname, the id is also included in the link for the delete method. Now you can immediately remove the comments added by asynchronous communication.

In the latter half, the specific execution contents of the js file are written. If the processing result is done, the if statement is used to conditionally branch whether or not the seller is a seller.

Supports turbolinks

If you use turbolinks together, the js file may not be read properly. For a solution to this, the article written by another person was helpful. Until I found this article, I didn't think that turbolinks was the cause in the first place, so it was an eye-opener.

How to solve js not working unless page is loaded with Rails @ryico

Today's stack

Until now, asynchronous communication by ajax was recognized that the posted comment data was returned in json. However, instead, I understood that the instance variable created during the create action is processed with jbuilder, converted to json, and returned. If you want to use records in the item table like this time, you can create @item.

Recommended Posts

Asynchronous communication in the interactive comment field
Rails refactoring story learned in the field
Posting function implemented by asynchronous communication in Rails
I tried to implement the like function by asynchronous communication
[Rails] I implemented the validation error message by asynchronous communication!