Using Rails 5 and Ajax: Comments will only be appended if a comment already exists

I have been working on a new feature that allows users to add comments to articles using Ajax. However, I have encountered an issue where the comment only gets rendered successfully through Ajax if there is already one existing comment. The database commits the comment without any rollback process.

If I refresh the page and try to create a second comment, the new comment will only appear after the second attempt.

Started POST "/articles/veniam-ipsum-eos-quas-aut-rerum-consequatur-at-velit-perferendis-odio/comments" for 103.252.202.198 at 2017-11-03 16:50:14 +0000
Processing by CommentsController#create as JS
  Parameters: {"utf8"=>"✓", "comment"=>{"content"=>"comment only appended if a comment exists"}, "commit"=>"Add Comment", "article_id"=>"veniam-ipsum-eos-quas-aut-rerum-consequatur-at-velit-perferendis-odio"}
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Article Load (0.6ms)  SELECT  "articles".* FROM "articles" WHERE "articles"."slug" = $1 LIMIT $2  [["slug", "veniam-ipsum-eos-quas-aut-rerum-consequatur-at-velit-perferendis-odio"], ["LIMIT", 1]]
   (0.2ms)  BEGIN
  SQL (0.6ms)  INSERT INTO "comments" ("content", "article_id", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["content", "comment only appended if a comment exists"], ["article_id", 189], ["user_id", 1], ["created_at", "2017-11-03 16:50:14.291049"], ["updated_at", "2017-11-03 16:50:14.291049"]]
   (2.5ms)  COMMIT

Comments controller

class CommentsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_article

  def create
    @comment = @article.comments.build(comment_params)
    @comment.user = current_user
      if @comment.save
        respond_to do |format|
          format.html do 
            flash[:success] = "Your comment was created successfully"
            redirect_to @comment.article
          end
            format.js
        end
        unless @comment.article.user == current_user
          Notification.create!(recipient: @article.user, actor: current_user, action: "posted", notifiable: @comment)
        end
      else
       respond_to do |format|
          format.html { redirect_to @comment.article, flash[:error] = "Unable to submit comment."}
        end
      end
  end

  private

    def set_article
      @article = Article.friendly.find(params[:article_id])
    end

    def comment_params
      params.require(:comment).permit(:content)
    end

end

Articles controller

  def show
      @comments = @article.comments.order("created_at DESC")
      @new_comment = @article.comments.new
  end

articles/show.html.erb

  <%= render 'comments/comment_form' %>
  <% if @comments.exists? %>
    <div id= "comment" >
      <%= render @comments %>
    </div>
  <% else %>
    <div class ="no-comments">
      <p> There are no comments yet.</p>
    </div>
  <% end %>

comments/_comment_form.html.erb

  <%= form_for [@article, @new_comment], remote: true do |f| %>
    <div class="form-group">
      <div class = "row">
        <div class= "col-md-9 col-sm-9 col-xs-12">
          <%= f.text_area :content, rows: 2, placeholder: "Write your comment...", class: 'form-control' %>
        </div>
        <div class= "col-md-3 col-sm-3 col-xs-12">
          <%= f.submit 'Add Comment', class: 'btn btn-md btn-default' %>
        </div>
      </div>
    </div>
  <% end %>

comments/_comment.html.erb

<%= comment.content %>

comments/create.js.erb

$('#comment').append("<%= escape_javascript (render partial: @comment) %>");

routes:

  resources :articles do
    resources :comments
  end

SOLUTION:

User PlanB provided a solution that worked, but I made some adjustments so that

<p> There are no comments yet.</p>
is removed after submitting the comment form.

articles/show.html.erb

  <div id="comment-form">
    <%= render 'comments/comment_form' %>
  </div>
  <div id = "comment-list" >
    <% if @comments.exists? %>
        <%= render @comments %>
    <% else %>
        <div id = "no-comments">
          <p> There are no comments yet.</p>
        </div>
    <% end %>
  </div>

comments/create.js.erb

$('#comment-list').append("<%= escape_javascript (render partial: @comment) %>");
$('#no-comments p').html('');

Answer №1

Consider trying this approach:

  <%= render 'comments/comment_form' %>
  <% if @comments.exists? %>
    <div class= "comment" >
      <%= render @comments %>
    </div>
  <% else %>
    <div class= "comment">
      <p> No comments have been added yet.</p>
    </div>
  <% end %>

Furthermore:

$('.comment').append("<%= escape_javascript (render partial: @comment) %>");

This is important because without the if statement creating

div#comment</code when there are no comments, </p>

<p><code>$('#comment').append("<%= escape_javascript (render partial: @comment) %>");
cannot append content to #comment as it does not exist in the HTML.

Answer №2

The issue might lie in your create method implementation. When you redirect to @comment.article, the create.js.erb file cannot be accessed. One way to address this is by modifying the create method as shown below:

def create
  @comment = @article.comments.build(comment_params)
  @comment.user = current_user
  if @comment.save
    respond_to do |format|
      format.html do 
        flash[:success] = "Your comment was successfully posted"
      end
      format.js
    end
    unless @comment.article.user == current_user
      Notification.create!(recipient: @article.user, actor: current_user, action: "posted", notifiable: @comment)
    end
  else
    respond_to do |format|
      format.html { redirect_to @comment.article, flash[:error] = "Failed to submit comment."}
    end
  end
end

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A guide on effectively mocking a Vuex store within the parentComponent of VueJS test-utils

I am currently using Jest in conjunction with vue-test-utils to test the reaction of a child component to an $emit event triggered by the parent component. The VueJS test-utils library offers a parentComponent option that can be utilized when mounting or ...

Is there a way to switch the cursor to a pointer when hovering over a bar on a ChartJS bar graph?

Here's the chart I created: createChart = function (name, contributions, idArray) { globalIdArray = idArray; var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: "bar", data: { lab ...

Retention of user-entered data when navigating away from a page in Angular

I need to find a way to keep the data entered in a form so that it remains visible in the fields even if the user navigates away from the page and then comes back. I attempted to use a solution from Stack Overflow, but unfortunately, it did not work as exp ...

The best way to avoid routing before certain async data in the Vuex store has finished loading

I am facing a challenge in my application where I require certain data to be loaded into the VueX store before routing can commence, such as user sessions. An example scenario that showcases a race condition is as follows: // Defined routes { name: &ap ...

Retrieve the child grid's data source in Kendo using Angular

I am currently working with a master detail grid in Kendo Grid and using the Angular version of the Kendo Grid. One interesting feature I have added is a custom button for adding a new record in each row of the grid. When this button is clicked, a new rec ...

Layered parallax scenery

I'm interested in creating a parallax effect similar to this example. https://medium.com/@PatrykZabielski/how-to-make-multi-layered-parallax-illustration-with-css-javascript-2b56883c3f27 However, I find the use of HAML and Coffeescript in the mentio ...

Executing complex queries in mongoose using the $or operator

I'm in search of an efficient way to create clean code for executing multiple complex queries. Within my MongoDB database, I have two collections: followers and events. The first query involves retrieving all followers associated with a specific use ...

Finding the correct column in a drop-down menu based on a table array using AngularJS

In my controller, I have data like this: $scope.operationData = [ { "label" : "Inventory", "labelType" : "Master Tables", "type" : "PROCESSOR", "outputStreams" : 1, "elementType" : "TABLE", "name" : ...

What is the best way to obtain the post id when making a JavaScript Ajax request?

I am currently developing a website similar to Stack Overflow for practice purposes. I am currently working on implementing the voting system. My goal is to have an Ajax call triggered when the upvote or downvote button is clicked, sending parameters such ...

Formik button starts off with enabled state at the beginning

My current setup involves using Formik validation to disable a button if the validation schema is not met, specifically for a phone number input where typing alphabets results in the button being disabled. However, I encountered an issue where initially, ...

Measuring the variable size of an array containing objects of a given class

Recently, I created a basic code/userscript to receive notifications about any changes on a specific website: function notifier(){ setTimeout(function () { location.reload(true); },60000) } function notiCounter() { console.log("Cou ...

The function of the React index key appears to be malfunctioning within the map function

I've been encountering issues despite using the index key and also attempted utilizing a unique id from JSON data, but unfortunately haven't found a solution yet. ERROR Warning: Each child in a list should have a unique "key" prop. const fa ...

Tips for running two elixir tasks consecutively?

Check out this piece of code: var gulp = require('gulp'), fs = require('fs'); gulp.task('taskOne', function() { return gulp.src('folder1/file1.js') .pipe(gulp.dest('folder2')); }); gulp.t ...

Is there a way to export a modified OBJ geometry from a Three.js scene?

Every time I make changes and export my Three.js scene with a SkinnedMesh model, the original imported model gets saved instead of the updated version. Despite rotating bones and adjusting morph targets, the exported model remains unchanged. Even though t ...

Selecting a date in Jade's date picker

I'm currently facing an issue with getting a datepicker to function properly using Jade in a node.js and express framework. Within index.jade, I am loading the necessary javascript files like this: link(type='text/css', href='css/ui-l ...

Using PHP to send asynchronous requests to the server can greatly enhance

I have almost completed my project, but I am facing an issue with reading the data sent to the server. function main() { jQ(document).on("keyup", "form input", function () { var data = new FormData(); var value = jQ(this).val(); da ...

Accessing a JSON array from PHP script with the help of jQuery

Recently, I delved into the world of jQuery and PHP by attempting to pass variables from Javascript to a PHP script using $.ajax. Despite my lack of expertise in this area, I managed to write some code that kind of worked. However, today I encountered a n ...

retrieving the value added to an array

I used a script to populate an array with image sources: var imgs = document.getElementsByTagName("img"); var imgSrcs = []; for (var i = 0; i < imgs.length; i++) { imgSrcs.push(imgs[i].src); } However, when I try to output this in PHP, it display ...

I am facing an issue with Recharts not occupying the full width in my Nextjs/Reactjs project. Despite setting it to 100% width, it does not behave as

I am currently working with Recharts in combination with Next.js and Tailwindcss. I decided to create my own barchart by copying a code snippet from Recharts, but encountered an issue where changing the height to aspect worked fine, however setting the wid ...

Choose all the HTML content that falls within two specific tags

Similar Question: jquery - How to select all content between two tags Let's say there is a sample HTML code as follows: <div> <span> <a>Link</a> </span> <p id="start">Foo</p> <!-- lots of random HTML ...