I've implemented a Star Rating System using this tutorial http://eighty-b.tumblr.com/post/1569674815/creating-an-ajaxified-star-rating-system-in-rails-3
The Ajax works perfectly, until I add the Javascript that Submits the form/saves the data after a given user makes a change.
For some reason, it will loop through all the elements on my Index Page, and Submits the correct integer value on the selected object, but then submits a bunch of NIL values on the rest. I only want it to update that ONE object.
(How can I submit the appropriate Book ID in the Javascript?)
New to rails please help :)
VIEWS
index.hrml.erb (books)
<% #books.each do |book| %>
<table id="book<%= book.id %>">
<tbody>
<tr>
<td>
<b><%= book.title %></b>
</td>
</tr>
<tr>
<td>
<%= book.release %>
</td>
</tr>
<tr>
<td id="rating"> #####This is my partial for my form
<%= render :partial => 'ratings/rating', :locals =>{:book => book} %>
</td>
</tr>
<tbody>
</table>
<% end %>
_rating.html.erb
Avg. Rating <%= book.average_rating %>
<%= form_for rating_ballot, :html => { :class => 'rating_ballot' }, :remote => true do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label("value_1", content_tag(:span, '1'), {:class=>"rating", :id=>"1"}) %>
<%= radio_button_tag("rating[value]", 1, current_user_rating == 1, :class => 'rating_button') %>
<%= f.label("value_2", content_tag(:span, '2'), {:class=>"rating", :id=>"2"}) %>
<%= radio_button_tag("rating[value]", 2, current_user_rating == 2, :class => 'rating_button') %>
<%= f.label("value_3", content_tag(:span, '3'), {:class=>"rating", :id=>"3"}) %>
<%= radio_button_tag("rating[value]", 3, current_user_rating == 3, :class => 'rating_button') %>
<%= f.label("value_4", content_tag(:span, '4'), {:class=>"rating", :id=>"4"}) %>
<%= radio_button_tag("rating[value]", 4, current_user_rating == 4, :class => 'rating_button') %>
<%= f.label("value_5", content_tag(:span, '5'), {:class=>"rating", :id=>"5"}) %>
<%= radio_button_tag("rating[value]", 5, current_user_rating == 5, :class => 'rating_button') %>
<%= hidden_field_tag("book_id", book.id) %>
<%= f.submit "Submit", class: "btn btn-primary"%>
<% end %>
create.js.erb & update.js.erb
$('table#book<%= #book.id%> td#rating').html("<%= escape_javascript(render :partial => 'ratings/rating', :locals => {:book => #book}) %>");
JAVASCRIPT
rating_ballot.js
####This Submits the Radio Button, but Loops through every Book on the page.
$(document).ready(function() {
###Submits the form (saves data) after user makes a change.
$('.rating_ballot').change(function() {
$('.rating_ballot').submit();
});
});
CONTROLLER
class RatingsController < ApplicationController
before_filter :current_user, only: [:create, :update]
respond_to :html, :js
def create
#book = Book.find_by_id(params[:book_id])
#rating = Rating.create(params[:rating])
#rating.book_id = #book.id
#rating.user_id = current_user.id
if #rating.save
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
end
def update
#book = Book.find_by_id(params[:book_id])
#rating = current_user.ratings.find_by_book_id(#book_id)
if #rating.update_attributes(params[:rating])
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
end
end
This is bcoz all the rating forms are submitted whenever any single rating is changed
Change your JS code as follows
$(document).ready(function() {
$('.rating_button').change(function() {
$(this).parents('form:first').submit();
});
});
Also above code will not work in JS loaded content.
i.e. your rating form will not be submitted once you add rating and that form is updated from create.js.erb or update.js.erb
Change it to
$(document).ready(function() {
$(document).on('change', '.rating_button', function(){
$(this).parents('form:first').submit();
});
});
Related
I'm building an events app using Rails 5.0 and have comments as a nested resource. Users can create and destroy comments, I'm trying to implement the edit/update function using Ajax/ remote: true so they can update a comment on the same page but it's not working. When I click on the edit link nothing happens. Here's the relevant code -
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #event
else
render 'new'
end
end
# GET /comments/1/edit
def edit
#event = #comment.event
#comment = #event.comments.find(params[:id])
respond_to do |format|
format.html { render :edit }
format.js {}
end
end
def show
end
def update
if #comment.update(comment_params)
redirect_to #event, notice: "Comment was successfully updated!"
else
render 'edit'
end
respond_to do |f|
format.html { redirect_to #event, notice: "Comment Successfully updated!" }
format.js # render 'comments/update.js.erb'
end
end
def destroy
#event = Event.find(params[:event_id])
#comment = #event.comments.find(params[:id])
#comment.destroy
redirect_to event_path(#event)
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:name, :body)
end
end
_comment.html.erb
<div class="comment clearfix">
<div class="comment_content">
<div id="comments" class="comment">
<p id="comment_name"><strong><%= #comment.name %></strong></p>
<p id="comment_body"><%= #comment.body %></p>
</div>
<p><%= link_to 'Edit', edit_event_comment_path(comment.event), id: "comments", remote: true %></p>
<p><%= link_to 'Delete', comment.event,
method: :delete,
class: "button",
data: { confirm: 'Are you sure?' } %></p>
</div>
</div>
update.js.erb
$('#comments').append("<%= j render #comment %>");
edit.js.erb
$('#comments').html("<%= j render 'form' %>");
_form.html.erb
<%= simple_form_for([#event, #comment], remote: true) do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
I've never implemented this action before using Ajax so I'm probably making a few schoolboy errors here. Any assistance appreciated.
You are calling edit method on controller with this
<%= link_to 'Edit', [comment.event, comment], id: "comment", remote: true %>
And you have no edit.js.erb
For updating your comment, you would have to create a form with it's action url pointing to your update method, and marking it as remote true. Then when you submit, it will reach update directly, there is no need to pass through edit method.
There is a method for creating forms with ajax option as default called form_with, you can check it's guide and documentation here:
http://guides.rubyonrails.org/working_with_javascript_in_rails.html#form-with
Updating answer after your question update
Your form would need to become something like this
<%= simple_form_for :comment, :url => "/events/#{comment.event_id}/comments/#{comment.id}", :method => :put do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
Sorry for the long title. I don't know how I got stuck this much.
I wanted to have a button (actually a link_to styled as a button) for FOLLOW / UNFOLLOW on remote. That's a Follow model where records are stored for Corporation and User (a User can follow a Corporation). The follow/unfollow links are on the Corporation show page.
<% if user_signed_in? %>
<% if Follow.where(corporation_id: #corporation.id, user_id: current_user.id).first.nil? %>
<%= link_to 'FOLLOW', {:controller => "follows", :action => "create", :user_id => current_user.id, :corporation_id => #corporation.id}, remote: true, :method => "post", class: "btns follow", id: "follow1" %>
<% elsif %>
<%= link_to 'UNFOLLOW', {:controller => "follows", :action => "destroy", :corporation_id => #corporation.id, :user_id => current_user.id }, remote: true, :method => "delete", class: "btns unfollow", id: "unfollow" %>
<% end %>
<% end %>
These are the controller actions:
def create
#corporation_id = params[:corporation_id]
#follow = Follow.new(follow_params)
#follow.save
respond_to do |format|
format.js {render :file => "/corporations/create.js.erb" }
end
end
def destroy
#corporation_id = params[:corporation_id]
attending.destroy
#attending is a method where the follow is defined. This is ok.
respond_to do |format|
format.js {render :file => "/corporations/destroy.js.erb" }
end
end
I'm rendering create.js.erb in corporations, since the change has to happen there. If I leave it as format.js, it'll search in the follows folder which is empty.
The create.js.erb look like this:
$("#unfollow").click(function(){
$("unfollow").replaceWith("<%= escape_javascript(render :partial => "corporations/profile/follow", :locals => {corporation_id: #corporation_id}) %>");
});
Btw, I tried .html instead of replaceWith, but it's not that.
The destroy.js.erb is similar. And the _unfollow.html.erb partial is like this:
<% if !#corporation.nil? %>
<%= link_to 'UNFOLLOW', {:controller => "follows", :action => "destroy", :corporation_id => #corporation.id, :user_id => current_user.id }, :method => "delete", class: "btns unfollow", remote: true, id: "unfollow" %>
<% else %>
<%= Rails.logger.info("First here") %>
<%= Rails.logger.info(corporation_id) %>
<%= link_to 'UNFOLLOW', {:controller => "follows", :action => "destroy", :corporation_id => corporation_id.to_i, :user_id => current_user.id }, :method => "delete", class: "btns unfollow", remote: true, id: "unfollow" %>
<%= Rails.logger.info("Now here...") %>
<% end %>
Without the first condition it just fires up an error the corporation_id (it's same with locales[:corporation_id]) is not defined and similar.
I have no idea what to try now... All the tutorials on the net are quite simple but the remote action is just one action in the controller where it needs to change, this has to go to another controller then back, then again to Follow... I'd really appreciate the help.
I have tried to fix this for some time, but I cannot find the solution. I have ajax controlling some on page tabs, which is working fine:
$("#feed-content").html("<%= escape_javascript(render(:partial => "#{#partial_name}")) %>");
But then I added will_paginate with endless scrolling, where I put the js that is controlling that in the same js.erb file as the above (sale.js.erb):
$("#feed-content").html("<%= escape_javascript(render(:partial => "#{#partial_name}")) %>");
$('#products').append('<%= escape_javascript(render(:partial => 'sale_content', :products => #products, :remote => true)) %>');
<% if #products.next_page %>
$('.pagination').replaceWith('<%= escape_javascript( will_paginate(#products)) %>');
<% else %>
$('.pagination').remove();
<% end %>
But that do not working. But each part work individually.
Then i tried this, but it still does not work (it only loads the if part):
<% if params[:feed]%>
$("#feed-content").html("<%= escape_javascript(render(:partial => "#{#partial_name}")) %>");
<% else %>
$('#products').append('<%= escape_javascript(render(:partial => 'sale_content', :products => #products, :remote => true)) %>');
<% if #products.next_page %>
$('.pagination').replaceWith('<%= escape_javascript( will_paginate(#products)) %>');
<% else %>
$('.pagination').remove();
<% end %>
the view
...
<p class="hero-description-dark local-nav-container">Sort by <%= link_to "popular", products_popular_path(:feed => "popular"), :remote => true, :class => "active"%> or <%= link_to "sale", products_sale_path(:feed => "sale"), :remote => true%></p>
Controller
def sale
products = Product.gender(current_user).available.includes(:likes)
#products = products.filter_by_categories(products).first(100).paginate(:page => params[:page], :per_page => 6)
#partial_name = "sale"
respond_to do |format|
format.html
format.json { render json: #products}
format.js
end
end
there is a missing <% end %> tag. I which that is not the problem. Try something like this:
<% if params[:feed].present? %>
$("#feed-content").html("<%= escape_javascript(render(:partial => "#{#partial_name}")) %>");
<% else %>
$('#products').append('<%= escape_javascript(render(:partial => 'sale_content', :products => #products, :remote => true)) %>');
<% if #products.next_page %>
$('.pagination').replaceWith('<%= escape_javascript( will_paginate(#products)) %>');
<% else %>
$('.pagination').remove();
<% end %>
<% end %>
I found a solution myself. I added ID's to my "remote true"-links like this. In this case "popular":
<p>Sort by <%= link_to "popular", products_popular_path(:feed => "popular"), :remote => true, :class => "active", :id => "popular"%>
Then in the corresponding popular.js.erb file I added an onclick event to the ajax that controls the tabs. This means that the ajax only runs when the link is clicked at not when the page is supposed to do the endless-scrolling part.
$("#popular").click(function() {
$("#feed-content").html("<%= escape_javascript(render(:partial => "#{#partial_name}")) %>");
});
$('#products').append('<%= escape_javascript(render(:partial => "#{#partial_name}")) %>');
<% if #products.next_page %>
$('.pagination').replaceWith('<%= escape_javascript( will_paginate(#products)) %>');
<% else %>
$('.pagination').remove();
<% end %>
There may be a better and cleaner way, but this worked for me. I have asked another question here that is almost the same as this one. Just if someone needs more details.
UPDATE:
The above solution required two clicks on link to render the partial. The code beneath should be used instead:
$('#popular').bind('ajax:complete', function() {
$("#feed-content").html("<%= escape_javascript(render(:partial => "popular_content")) %>");
});
I am making a transition from Rails 2 to Rails 3.
_role_item.html.erb view:
<% if #employee.has_role?(role.id) %>
<%= link_to image_tag('pman/checkbox_unchecked.jpg'), action: 'add_role', :employee_id => #employee.id, :role_id => role.id %>
<% end %>
Clicking the checkbox should assign a role to that Employee and redirect back to the partial page which contains a role_list and a privilege_list.
In my controller I have:
def add_role
#employee = Employee::OldEmployee.find(params[:employee_id])
#employee.add_role(params[:role_id])
redirect_to :action => 'employee_privileges', :employee_id => #employee.id
end
The view which has both partials, _employee_privileges.html.erb:
<h3> <%= #employee.full_name %> (<%= #employee.initials %>) <%= #assign_roles %> - PRIVILEGES & ACCESS</h3>
<br>
<table><tr><td valign=top>
<%= render :partial => 'role_list' %>
</td><td width=50>
</td><td valign=top>
<%= render :partial => 'privilege_list' %>
</td></tr></table>
<br>
employee_privileges.js.erb:
$("#roles").html("<%= j(render partial: 'employee_privileges') %>");
Function within controller:
def
employee_privileges
#employee = Employee::OldEmployee.find(params[:employee_id])
#roles = Acl::Role.find :all
#modules = Acl::Module.find :all
#module_privileges = Array.new
#general_privileges = Acl::Privilege.find :all, :conditions => 'module_id IS NULL'
if !#general_privileges.empty?
#module_privileges << [nil, nil, nil, #general_privileges]
end
#modules.each do |m|
if !m.privileges.empty?
#module_privileges << [m.id, m.name, m.description, m.privileges]
end
end
#assign_roles = flash[:assign_roles]
#render :partial => 'employee_privileges'
end
Currently, when clicking on the checkbox, I receive the error:
Missing template acl/acl/employee_privileges, application/employee_privileges with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/home/alex/Intranet_update/app/views"
Any help would be appreciated.
Please provide the relative path in the for the partial you want to render.
$("#roles").html("<%= j(render partial: 'employee_privileges') %>");
option like render partial: '/folder_path/employe_privileges'
I have a Rails app with listing of leads in a table. In one of the collumns I display status of a lead in a drop down menu. I want to enable changing this status of the lead on changing the value selected in the drop down.
This is what I tried:
The code to display the form in a table cell:
<% #leads.each do |lead| %>
<tr>
<td><%= lead.id %></td>
<td><%= form_for(lead,:url => 'update_lead_status') do |f| %>
<div class="field">
<%= f.select :status, ["to_call","called","confirmed","lite"], :selected => lead.status, onchange: "this.form.submit();" %>
</div>
<% end %>
</td>
my update_lead_status method in leads controller:
#PUT
def update_lead_status
#lead = Lead.find(params[:id])
respond_to do |format|
# format.js
if #lead.update_attributes(params[:lead])
format.html { redirect_to leads_url, notice: 'Lead was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #lead.errors, status: :unprocessable_entity }
end
end
end
Also I want the submission to be Ajax style without refreshing.
Set form id and then submit form
<%= form_for(lead,:url => 'update_lead_status',:html=>{:id=>'lead_form'}) do |f| %>
<%= f.select :status, ["to_call","called","confirmed","lite"], :selected => lead.status, onchange: "$('#lead_form').submit();" %>
<% end %>