Rails 4 Ajax call only works once - javascript

I have an app where you can post Links. Each Link has_many Comments
I've set up ajax where a user can upvote a particular comment. Currently, a user can successfully upvote a comment via ajax the first time, but if the user then attempts to upvote a different comment on the same page then the ajax breaks and the value doesn't get updated until the page is refreshed.
comments_controller.rb:
def comment_upvote
#comment = Comment.find(params[:id])
#link = Link.where(id: #comment.link_id)
#comment.upvote_by current_user
respond_to do |format|
format.html {redirect_to link_path(#link)}
format.js {}
end
end
views/comments/_comment.html.erb:
<div class="well">
<h2><%= comment.title %></h2>
<p class="text-muted">Added by <strong><%= comment.author %> <%= comment.author_last_name %></strong> on
<%= l(comment.created_at, format: '%B, %d %Y %H:%M:%S') %></p>
<blockquote>
<p><%= comment.body %></p>
</blockquote>
<p><%= link_to 'reply', new_comment_path(parent_id: comment.id, link_id: #link.id) %></p>
<%= link_to pointup_comment_path(comment.id), method: :put, remote: true do %>
+
<% end %>
<div id="comment-votes">
Votes: <%= comment.get_upvotes.size %>
</div>
views/comments/comment_upvote.js.erb:
$('#comment-votes').html("<%= j render "upvotes", locals: { #comment => #comment } %>")
views/comments/_upvotes.html.erb:
Votes: <%= #comment.get_upvotes.size %>
Is there an easy way to fix this? Let me know if you need extra detail.

The problem here is that there are many divs with id comment-votes. When you try to get the element by id, it always get the same div.
To solve this problem you need to make the id unique per comment.
views/comments/_comment.html.erb:
<div id="comment-votes-<%= comment.id %>">
Votes: <%= comment.get_upvotes.size %>
</div>
After setting the unique comment ids. You just need to change the ajax call.
views/comments/comment_upvote.js.erb:
$("#comment-votes-<%= #comment.id %>").html("<%= j render "upvotes", locals: { #comment => #comment } %>")

Related

Sending update request in Rails 5 edit form using AJAX

I'm having an issue with an edit form on my page to update and save attributes using AJAX in Ruby on Rails 5. The goal is to render the edit form in the same page and then after editing the information submit the form and have the info show back up. Getting the edit form to show up on the page works perfectly fine but after that it just does nothing.
I have a feeling it has something to with where the form is sending to but I can't seem to figure it out. I have searched all over and it seems there are lots of answers out there for rails 3/4 but the request is different for rails 5. From all the examples I can find, my code looks correct, but clearly I'm missing something. I'm still a beginner rails developer but from my understanding the edit form should send an update request to the controller but it doesn't seem to do so. Any help is appreciated. Code below.
Controller
def create
#properties = Property.new(property_params)
if #properties.save
flash[:success] = "Property created successfully!"
redirect_to properties_path
else
render 'new'
end
end
def edit
#properties = Property.find(params[:id])
respond_to do |format|
format.html { #properties.save }
format.js
end
end
def update
#properties = Property.find(params[:id])
#property.update_attributes(property_params)
respond_to do |format|
format.html { redirect_to property_path }
format.js
end
end
View
<%= link_to fa_icon("edit", text: "Edit Property"),
edit_property_path(#properties), remote: true, class: "btn btn-
primary btn-large btn-ouline" %>
edit.js.erb
$('div#show-edit').replaceWith('<%= j render("edit_prop_form") %>');
update.js.erb
$('#edit_property_<%= #properties.id %>').replaceWith('<%= j render("properties_display") %>');
_edit_prop_form.html.erb partial
<%= bootstrap_form_for(#properties, layout: :horizontal, control_col: "col-sm-4", remote: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_field :company, class: 'form-control' %>
<%= f.text_field :address1, class: 'form-control' %>
<%= f.text_field :address2, class: 'form-control' %>
<%= f.text_field :city, class: 'form-control' %>
<%= f.text_field :state, class: 'form-control' %>
<%= f.text_field :zipcode, class: 'form-control' %>
<%= f.form_group do %>
<%= f.submit "Edit", class: "btn btn-primary" %>
<% end %>
<% end %>
HTML that edit form renders
<form role="form" class="form-horizontal" id="edit_property_99"
action="/properties/99" accept-charset="UTF-8" data-remote="true"
method="post"><input name="utf8" type="hidden" value="✓"><input
type="hidden" name="_method" value="patch">
TL;DR Edit form renders on page from initial edit button but "Edit" button on the form does not update and save edited info and display original div.
As mr12086 pointed out, in my update method #property should have been #properties. After making the change all worked as expected.

Counter span on my like button not updating with ajax

I have followed the answers suggested on other questions on SO (this question and this question ), but for some reason I can't get my like button counter to work properly with ajax. My counter changes only after a refresh, any help is appreciated thanks!
meals_controller.rb
def upvote
#meal.upvote_from current_user
if request.xhr?
render json: { count: #meal.get_upvotes.size, id: params[:id] }
else
redirect_to meals_path
end
end
def unlike
#meal.unliked_by current_user
redirect_to meals_path
end
routes.rb
Rails.application.routes.draw do
devise_for :users
root 'home#index'
resources :meals do
member do
put "like" => "meals#upvote"
put "unlike" => "meals#unlike"
end
end
#upvote.coffee
$(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
$(".votes-count[data-id=#{data.id}]").text data.count
return
#index.html.erb
<p id="notice"><%= notice %></p>
<h1>Listing Meals</h1>
<% #meals.each do |meal| %>
<%= meal.title %>
<br>
<%= meal.descriptionttext %>
<%= link_to like_meal_path(meal), class: "vote", method: :put, remote: true do %>
<button>
<span class="votes-count" data-id="<% meal.id %>"><%= meal.get_likes.size %></span>
</button>
<% end %>
<%= link_to unlike_meal_path(meal), method: :put do %>
<button type="button">
<span><%= meal.get_upvotes.size%></span>
</button>
<% end %>
<br>
<% end %>
<br>

Rendering partial with remote: true if i have few same ids

I have a problem with rendering data on my page through remote: true.
I have a comments and subcomments (comments to the comments). When I click "Comment" button under the second Post, a comment appears below the first because I have a few same ids "comment-list" in my loop. How I should fix this? P.S Sorry for my english :)
Here is how look my Post and Comments form:
<% #posts.each do |post| %>
<li class="post">
...
<% if current_user == post.user %>
<%= link_to "delete", post, method: :delete, remote: true %>
<% end %>
</li>
<div id="comments-list">
<%= render partial: 'post_comments/comments', locals: { post: post } %>
</div>
<% if user_signed_in? %>
<div id="comment-form">
<%= render partial: 'post_comments/new_comment_form', locals: { post: post } %>
</div>
<% end %>
And here is how looks my create.js.erb
$('#comments-list').html("<%= j (render partial: 'post_comments/comments', locals: { post: #post }) %>");
What I've done in a similar situation is make your div IDs specific to each post:
<div id="comments-list-<%= #post.id.to_s %>">
and then in your jquery code specify the relevant post:
$('#comments-list-<%= #post.id.to_s %>').html("<%= j (render partial: 'post_comments/comments', locals: { post: #post }) %>");
Let create a wrapper specifying your post
<% #posts.each do |post| %>
<div id="post_<%= post.id %>">
<!-- Your current code here -->
<div>
<% end %>
Use comments-list as class instead (the same apply for comment-form)
<div class="comments-list">
<%= render partial: 'post_comments/comments', locals: { post: post } %>
</div>
Finally your jquery selector in erb will be:
$("#post_<%= #post.id %> .comments-list")

Error display on ajax form in rails not working

I'm using an inline form for users to submit data to a table (note that I'm using CSS rather than html tables to achieve this). When an error is returned by the verification in the model, I want to highlight the input field using the Bootstrap error class and put the error message below the appropriate form field. I'm using AJAX to submit the form.
I'm having problems, this is what I have so far:
Controller:
def create
#travel = Travel.new(params[:travel])
#travel[:user_id] = current_user.id
convert_date # and return
if #travel.save
flash[:notice] = "Successfully saved trip"
#travels = Travel.where("user_id = ?",current_user)
respond_to { |format| format.js }
end
end
JS view:
<% if #travel.errors.any? %>
<% #travel.errors.full_messages.each { |msg| logger.debug(msg) } %>
<% #travel.errors.messages.each do |k,v| %>
<% logger.debug("#tf_#{k}") %>
$(<%= "#tf_#{k}" %>).insertAdjacentHTML("afterbegin","<span class="control-group error"><span class="controls">");
$(<%= "#tf_#{k}" %>).insertAdjacentHTML("beforeend","</span></span>");
$(<%= "#error_#{k}" %>).val("<%= "#{k} #{v}" %>");
<% end %>
<% else %>
$(":input:not(input[type=submit])").val("");
$("#travels_list").html("<%= escape_javascript( render(:partial => "travels") ) %>");
<% end %>
form partial:
<%= form_for #travel, :remote => true, :html => {:class => "form-inline", :id => "new-travel-form"} do |f| %>
<div class="row-fluid">
<span id="tf_city"><%= f.text_field :city, :placeholder => "London, UK", :class => "span3" %></span>
<span id="tf_arrive_date"><%= f.text_field :arrive_date, :class => "span2" %> </span>
<span id="tf_leave_date"><%= f.text_field :leave_date, :class => "span2" %> </span>
<span id="tf_notes"><%= f.text_field :notes, :placeholder => "e.g. staying at the Hilton", :class => "span3" %></span>
<%= f.submit "save", :class => "btn btn-primary span1" %>
</div>
<% end %>
<div class="row-fluid error" id="error_expl">
<span id="error_city" class="help-inline span3"></span>
<span id="error_arrive_date" class="help-inline span2"></span>
<span id="error_leave_date" class="help-inline span2"></span>
<span id="error_notes" class="help-inline span3">test</span>
</div>
The logger.debug in the JS is firing, so I know that the error is getting passed to the JS, but the insertAdjacentHMTL() doesn't seem to be working.
maybe this is not going to answer your question but i hope it could help to improve your code.
First, according to your code i guess you should have some associations like this in the model User:
has_many :travels
And in your model Travel:
belongs_to :user
So, in order to improve your code you can have this in your controller:
respond_to :js, only: [:create]
def new
#travel = current_user.travels.new
end
def create
#travel = current_user.travels.new(params[:travel])
if #travel.save
flash[:notice] = "Successfully saved trip"
#travels = current_user.travels
else
# I guess you should have some code here in case any validation fail. If you dont
# have validations for the model Travel, you don't need the if statement here.
end
end
About your problem, can you clarify what is "#tf_#{k}"?

In my comment app when i refresh the page comments disappears

My comment app works ,but the only problem is whenever i refresh the page the comments disappear.In the log it shows the body is inserted in the comments table(it is saved).What am i doing wrong here?Any help will be appreciated.Thank you in advance.
View#show
<div id="comments"></div>
<%= form_for :comment,:remote => true,:url=> {:controller=>"comments",:action=>"create"},:html => { :id => 'new-comment'} do |f| %>
<%= f.text_area(:body) %>
<div class="errors"></div>
<%= f.submit "post" %>
<% end %>
Comment controller
class CommentsController < ApplicationController
respond_to :js
def create
#deal=Deal.find(1)
#comment =#deal.comments.build(params[:comment])
#comment.save
respond_with( #comment, :layout => !request.xhr? )
end
def show
#comment=Comment.all
#deal=Deal.find(1)
#comment=#deal.comments
end
end
create.js.erb
$('#comments').append("escape_javascript(#comment.body)");
I don't see where your comments are being display in your show template.
How about something like this?
<div id="comments">
<% #comments do |comment| %>
<%= comment.body %>
<% end %>
</div>
<%= form_for :comment,:remote => true,:url=> {:controller=>"comments",:action=>"create"},:html => { :id => 'new-comment'} do |f| %>
<%= f.text_area(:body) %>
<div class="errors"></div>
<%= f.submit "post" %>
<% end %>
Note, you need to set #comments in the controller or use another method of getting comments, e.g. #view = View.find(params[:id]) and <%= #view.comments.each do |comment| %>...
I guess the comment has a belongs_to relationship with the post that is not assigned.
In your form you should add
<%= f.hidden_field :post_id, #post.id %>
If you want to play by the book, post_id should be attr_protected and instead assign it manually in the comments controller
#comment = Comment.new(params[:comment])
#comment.post_id = params[:comment][:post_id]
#comment.save

Categories

Resources