I'm trying to allow users to favorite posts and then it show them sort of of interaction through AJAX, but it's not working.
The error I'm getting in the console is:
ActionView::Template::Error (undefined local variable or method `post_item' for #<#<Class:0x007fecb2a3d5f8>:0x007fecb2a357e0>):
The button is being rendered through a partial:
<%= render "shared/fave_form", post_item: post_item %>
Here's the code for the button (shared/_fave_form.html.erb):
<% if current_user.voted_on?(Post.find(post_item)) %>
<%= link_to "unlike", vote_against_post_path(post_item.id), :remote => true, :method => :post, :class => "btn") %>
<% else %>
<%= link_to "like", vote_up_post_path(post_item.id), :remote => true, :method => :post, :class => "btn") %>
<% end %>
Here's the toggle.js.erb file:
$("#fave").html("<%= escape_javascript render('fave_form') %>");
When you render the partial using toggle.js.erb it is not getting locals value post_item, you have to provide it in also.So, your js code should be something like following
$("#fave").html("<%= escape_javascript(render :partial=>"fave_form", locals: {post_item: post_item}).html_safe %>);
I guess you are using some ajax call and then your toggle.js.erb so in your toggle action you must specify value to post_item, lets make it instance variable #post_item so that we can use it in toggle.js.erb.
$("#fave").html("<%= escape_javascript(render :partial=>"fave_form", locals: {post_item: #post_item}).html_safe %>);
The partial is using a local variable, so pass post_item as a local:
<%= render :partial => "shared/fave_form", :locals => {post_item: post_item} %>
Related
I want to render a modal form and be able to render it on differents places; hence I want to be able to change the order_id that is required to create a new feedback on server side (I use AJAX).
However, my render partial does not work. I get the following error:
undefined local variable or method `user_order_id' for #<#<Class:0x007fec6be26af8>:0x007fec6bf1b2d8>
any Idea ?
index.html.erb
[...]
<%= render partial: 'feedback_modal', locals: {user_order_id: #user_last_order} %>
[...]
_feedback_modal.html.erb
<%= simple_form_for(#review, method: :post, url: review_path(current_user.id, user_order_id), remote: true) do |f| %>
<%= f.input :rating, collection: [1,2,3,4,5], prompt: "Rate this meal", class: "col-sm-3" %>
<%= f.label :comment, "Your comments" %>
<%= f.text_area :comment, class: "commentaire" %>
<%= f.button :submit ,"Send Feedback", class: "btn" %>
<% end %>
controller:
[...]
#user_last_oder = Order.where(user_id: current_user.id, status: nil).last.id
[...]
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 have a page that renders multiple forms. Currently, when the user submits any one of these forms, it updates (via ajax) a div on the same page with the content from the form that was just submitted.
I also want to remove() the form element that was just submitted after the ajax post request is completed. However, I need to be able to access that specific form ID within the js.erb file to do so.
Since my page has x number of forms rendered dynamically, I cannot simply access an instance variable in my js.erb.
Page:
<% for peer_review in #peer_reviews %>
<%= render :partial => 'form', :locals => { :peer_review => peer_review } %>
<% end %>
<div id="completed_peer_reviews">
<%= render 'completed_peer_reviews' %>
</div>
The #peer_reviews instance variable contains an array of new PeerReview objects already containing some data.
Form:
<div id="peer_review_form_<%= peer_review.reviewee_id %>">
<%= form_for peer_review, :html => { :method => "post" }, :remote => true do |f| %>
<%= f.error_messages %>
<p>
Peer Review for: <%= User.find(peer_review.reviewee_id).name %><br />
</p>
<p>
<%= f.label :rating %>:
<%= f.select :rating, [1, 2, 3, 4, 5], { :include_blank => 'None' } %>
</p>
<p>
<%= f.label :review %><br />
<%= f.text_area :review %>
</p>
<%= f.hidden_field :user_id, :value => peer_review.user_id %>
<%= f.hidden_field :reviewee_id, :value => peer_review.reviewee_id %>
<%= f.hidden_field :review_period_id, :value => peer_review.review_period_id %>
<p><%= f.submit "Submit" %></p>
<% end %>
</div>
js.erb:
$("#completed_peer_reviews").html("<%= escape_javascript(render('completed_peer_reviews')) %>");
I was hoping to just add another line to the js.erb file that removes the form element that just triggered the execution of the js.erb file like so:
$("#peer_review_form_<%= peer_review.reviewee_id %>").remove();
How should I actually be referencing peer_review.reviewee_id here? Or should I be taking a completely different approach?
This is one of the classic issues of RJS templates.
Quick answer:
If you simply want to solve the problem, you could pass along some temporary id to identify the form. e.g:
# in the index
<% #peer_reviews.each.with_index do |peer_review, i| %>
<%= render :partial => 'form',
:locals => { :peer_review => peer_review, :i => i } %>
<% end %>
# then in the form (note, you don't need to specify POST in a form_for)
<div id="peer_review_form_<%= i %>">
<%= form_for peer_review, :remote => true do |f| %>
<%= hidden_field_tag 'temp_id', i %>
# finally in the create js.erb
$("#peer_review_form_<%= params[:temp_id] %>").remove();
Longer Answer:
That being said, while RJS templates were "the Rails way" for a long time, they've since fallen out of favor.
The more modern method is typically client side JS templates with a JSON API, rather than running server generated JS templates (RJS). This has a lot of advantages, one being that the DOM binding issue you're having right now no longer exists.
This is an example of how you might do this with pure jQuery, but there are many templating options out there.
<script id="peer_review_tmpl" type="text/x-jquery-tmpl">
<div class="completed_peer_review">
<p>${review}</p>
...
</div>
</script>
Then you'd create a handler and bind it to a successful ajax response. This would require that your peer_reviews#create action responded to JSON:
$('form.new_peer_review').bind("ajax:success", function(data) {
// remove the form which triggered the creation
$(this).remove();
// then render the data into a template, and append it to the list
$("#peer_review_tmpl").tmpl(data).appendTo("#completed_peer_reviews");
});
In a Rails 3 application, I have a "table" partial that contains a data entry form table, plus a separate smaller form (mostly hidden fields) below it to clear the table data. I have a third form underneath the partial to add a new column to the table contained in the partial. The page loads fine. The small form to clear the table data works, and refreshes the partial as it is supposed to. BUT, When I submit the add-new-column form, I get this routing error:
ActionView::Template::Error (No route matches {:controller=>"outcome_results", :action=>"clear_table"}):
70: </table>
71: <%= submit_tag "Save" %>
72: <% end %>
73: <%= form_tag url_for(:controller => 'outcome_results', :action => 'clear_table'), :id => "clear_data_table_link", :remote => true do %>
74: <%= hidden_field_tag "subgroup_id", subgroup_id %>
75: <%= hidden_field_tag "outcome_id", #selected_outcome_object.id %>
76: <%= hidden_field_tag "timepoint_id", timepoint_id %>
app/views/outcome_results/_table.html.erb:73:in `_app_views_outcome_results__table_html_erb__204353865_18893424_435027370'
app/controllers/outcome_columns_controller.rb:36:in `block (3 levels) in create'
app/controllers/outcome_columns_controller.rb:35:in `block (2 levels) in create'
app/controllers/outcome_columns_controller.rb:33:in `create'
Line 72 is the end tag of the first (table/data entry) form.
Line 73 is the form tag for my clear-table-data form which works fine on its own - no routing errors there.
My routes.rb is crazy long but contains this line:
match 'projects/:project_id/studies/:study_id/clear_table' => 'outcome_results#clear_table'
The add-new-column form looks like this:
<div id="outcome_column_validation_message"></div>
<%= form_for #outcome_column, :action => :create, :remote => true, :id=>"outcome_columns_form" do |f| %>
<%= hidden_field_tag "outcome_id", !#selected_outcome_object.nil? ? #selected_outcome_object.id : nil %>
<%= hidden_field_tag "subgroup_id", !#selected_timepoint.nil? ? #selected_timepoint : 0 %>
<%= hidden_field_tag "timepoint_id", !#selected_subgroup.nil? ? #selected_subgroup : 0 %>
<div class="field">
Custom Column Title: <%= f.text_field :name %> Description: <%= f.text_field :description %> <%= f.submit "Add Custom Column" %>
<% end %>
and the format section of the "create" action in the "outcome_column" controller looks like this:
respond_to do |format|
format.js {
render :update do |page|
page.replace_html 'outcome_results_table', :partial => 'outcome_results/table'
page['outcome_columns_form'].reset
page.replace_html 'outcome_column_validation_message', ""
end
}
end
I can post more code if it would help... anyone have any ideas about this routing error?
Thanks in advance.
The route would take two arguments: a project_id and a study_id. This is not matching the route because you have not passed through these two parameters to the url_for in your form_tag.