So I wanted to play with a star ratings page, where I have 5 rating questions and you can rate once (create) and update your rating at any given time. The rating form is ajaxified but the problem is the form element stays as a Create method after submission instead of an update method.
I followed a pretty cool tutorial by eighty-b but there is one part that is missing about the form being updated to the proper method.
Here is the code
I have a form in a partial rating_questions/_quick_form.html.erb
with a nested form for each rating form. Note that the form route is using a helper to define if it should be a create or and update action
<div class="rating_questions">
<% @rating_questions.each do |rating_question| %>
<%= render partial: "rating_questions/rating_form", :locals => {:rating_question => rating_question} %>
<% end %>
</div>
The rating_form.html.erb
partial
# EDIT: I added the temporary random id in an instance variable to make sure the form id and hidden_field have the same reference
<% @rand_id = SecureRandom.hex %>
<%= form_for(rating_ballot(rating_question), :remote => true, :html => { :class => 'rating_ballot', :id => @rand_id}) do |f| %>
<div class="rq"><%= rating_question.title %></div>
<%= hidden_field_tag :temp_id, @rand_id %>
<%= f.label("value_#{rating_question.id}_1", content_tag(:span, '1'), {:class => "rating", :id => "1"}) %>
<%= radio_button_tag("rating[value]", 1, current_user_rating(rating_question) == 1, :class => 'radio_button', :id => "rating_value_#{rating_question.id}_1") %>
... (the other rating radio buttons) ...
<%= f.hidden_field("rating_question_id", :value => rating_question.id) %>
<%= f.submit :Submit, :id => "rating_submit" %>
<% end %>
Then in my create.js.erb
I added the line to replace the form with the partial
$('#<%= params[:temp_id] %>').replaceWith("<%= j render(partial: 'rating_questions/rating_form', locals: {:rating_question => @rating_question}) %>");
The helper methods for defining if the form should be a create or an update if there is an existing record
def rating_ballot(rating_question)
if @rating = current_user.ratings.find_by_rating_question_id(rating_question.id)
[rating_question, @rating]
else
[rating_question, current_user.ratings.new]
end
end
def current_user_rating(rating_question)
if @rating = current_user.ratings.find_by_rating_question_id(rating_question.id)
@rating.value
else
"N/A"
end
end
and my ratings_controller.rb
that calls for create.js.erb
and update.js.erb
def create
@rating_question = RatingQuestion.find_by_id(params[:rating_question_id])
@rating = Rating.new(params[:rating])
@rating.user_id = current_user.id
if @rating.save
respond_to do |format|
format.html { redirect_to rating_questions_path, :notice => "Your rating has been saved"}
format.js
end
end
end
def update
@rating_question = RatingQuestion.find_by_id(params[:rating_question_id])
@rating = current_user.ratings.find_by_rating_question_id(@rating_question.id)
if @rating.update_attributes(params[:rating])
respond_to do |format|
format.html { redirect_to rating_questions_path, :notice => "Your rating has been updated"}
format.js
end
end
end
Obviously I only want to update the form that was just submitted and not the other ones. Any idea on how to reload the form with the proper method using Javascript in the create.js.erb and the update.js.erb?
Thank you very much!
EDIT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (See the edits above)
I went ahead with your suggestions and I added the random id to the form and changed the create.js.erb, but I encountered problems:
1) After the ratings forms are replaced, the Javascript no longer works on that partial.
Here is also the coffee script for dynamic interactions of the stars (actually the form radio labels)
$ ->
# Add the "Bright class" to the stars for each checked radio button before the document is ready.
$("form.rating_ballot").each ->
checkedId = undefined
checkedId = $(this).find("input:checked").attr("id")
$(this).find("label[for=" + checkedId + "]").prevAll().andSelf().addClass "bright"
$(document).ready ->
# makes stars glow on hover.
$("form.rating_ballot > label").hover (-> # mouseover
$(this).prevAll().andSelf().addClass "glow"
), -> # mouseout
$(this).siblings().andSelf().removeClass "glow"
# makes stars stay glowing after click.
$("form.rating_ballot > label").click ->
$(this).siblings().removeClass "bright"
$(this).prevAll().andSelf().addClass "bright"
# Submits the form (saves data) after user makes a change.
$(".rating_ballot").change ->
$(this).submit()
EDIT 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The fact that the update action was submitting all the form was corrected.