Controller not responding to Ajax - ruby-on-rails

In the home page of my application I iterate over a collection of tennis players (#atp_ranks), and for each tennis player I create a table row with his attributes and a button which allows the current user to enlist that tennis player:
<table>
<thead>
<tr>
<th> Rank </th>
<th> Player </th>
<th> Points </th>
<th id="atp_count" class="tennis_stats"> <%= current_user.atp_ranks.count %> selected </th>
</tr>
</thead>
<tbody>
<% #atp_ranks.each do |tennis_player| %>
<tr id="tennist-<%= tennis_player.ranking %>">
<td class="atpranking"> <%= tennis_player.ranking %> </td>
<td class="atpname"> <%= tennis_player.name %> </td>
<td class="atppoints"> <%= tennis_player.points %> </td>
<% unless Time.now.month == 12 %>
<td>
<div id="atpenlist_form_<%= tennis_player.id %>">
<% if current_user.atpenlisted?(tennis_player) %>
<%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id), html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
<% end %>
<% else %>
<%= form_for(current_user.atp_selections.build, remote: true) do |f| %>
<div><%= hidden_field_tag :atp_id, tennis_player.id %></div>
<%= f.submit "Choose", class: "btn btn-primary btn-sm" %>
<% end %>
<% end %>
</div>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
In order to give the user the opportunity to immediately see the result of submitting the form, I added remote: true in each form, and saved these forms as partials in app/views/atp_selections.
Then I created in the above directory the create.js.erb and destroy.js.erb files. Below is the content of the create.js.erb file:
$("#atp_count").html('<%= current_user.atp_ranks.count %> selected');
$("#atpenlist_form_<%= #tennist.id %>").html("<%= escape_javascript(render('atp_selections/atpdiscard')) %>");
The jQuery code should manipulate the atp_count id and the atpenlist_form_<%= #tennist.id %> id, that is the id of the fourth th tag, and the id of the div containing the button's forms.
Below is an extract of my atp_selections controller, which is too long to report it entirely:
def create
#tennist = AtpRank.find(params[:atp_id])
rankings = current_user.atp_ranks.pluck(:ranking)
atp_selections = current_user.atp_selections
wta_selections = current_user.wta_selections
if atp_selections.count <= 15 && wta_selections.count < 16
if (1..5).include?(#tennist.ranking) && (rankings & (1..5).to_a).size == 0
current_user.atpenlist(#tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
elsif (6..15).include?(#tennist.ranking) && (rankings & (6..15).to_a).size < 3
current_user.atpenlist(#tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
...
As you can see, the create action of the atp_selections controller is made of multiple if-else statements that respond to enlistment rules. However, what is important here is that in each condition I included the required code with the respond_to method for requests to be handled by Ajax.
However the controller does not respond to Ajax, and changes to atp_count and atpenlist_form_<%= #tennist.id %> are visible only after a page refresh.
The rails console reports the following error:
Rendered atp_selections/create.js.erb (223.6ms)
Completed 500 Internal Server Error in 695ms (ActiveRecord: 83.2ms)
ActionView::Template::Error (undefined local variable or method `tennis_player' for #<#<Class:0x00000005948748>:0x0000000593f648>):
1: <%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id),
2: html: { method: :delete }, remote: true) do |f| %>
3: <%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
4: <% end %>
app/views/atp_selections/_atpdiscard.html.erb:1:in `_app_views_atp_selections__atpdiscard_html_erb__451019467450256030_46643760'
app/views/atp_selections/create.js.erb:2:in `_app_views_atp_selections_create_js_erb__4477173780394533370_46811020'
tennis_player is the variable of the iteration and it seems as if it is not accepted when it is imported from a rendered partial.

When you call the same partial from within the js.erb file, tennis_player is not declared, and so you get the error undefined variable.
So you need to pass #tennist as tennis_player inside your partial from create.js.erb:
$("#atpenlist_form_<%= #tennist.id %>").html("<%= j render 'atp_selections/atpdiscard', locals: {tennis_player: #tennist} %>");

Related

How to use Rails 5.2 form_with to trigger a specific action?

My application needs to duplicate a Skill (from skills index) as many times the user needs it in his cart. So I decided to trigger the add-to-cart method of the skills_controller when the related form, including the number of duplicates and the Skill's id, is submitted. For this purpose, I added counter to the strong parameters of skills_controller.
Unfortunately, I am missing something to correctly setup the form: when submitted, it triggers the create method. Here is the code:
routes.rb extract
resources :skills, :path => "variables" do
resources :values_lists
member do
post :add_to_cart
get :create_values_list
get :upload_values_list
get :remove_values_list
end
collection do
get :index_all
end
end
skills_controller.rb method
def add_to_cart
#template_skill = Skill.find(params[:id])
iterations = params[:skill][:counter].to_i
until iterations == 0
#skill = #template_skill.deep_clone include: [:translations, :values_lists]
#skill.business_object_id = session[:cart_id]
#skill.template_skill_id = #template_skill.id
#skill.code = "#{#template_skill.code}-#{Time.now.strftime("%Y%m%d:%H%M%S")}-#{iterations}"
#skill.is_template = false
#skill.save
iterations -= 1
end
#business_object = BusinessObject.find(session[:cart_id])
redirect_to #business_object, notice: t('SkillAdded2BO') # 'Skill successfully added to business object'
end
index.html.erb table content
<tbody>
<% #skills.each do |skill| %>
<tr data-href="<%= url_for skill %>">
<% if not session[:cart_id].nil? %>
<td>
<%= form_with model: #skill, :action => "add_to_cart", :method => :post, remote: false do |f| %>
<%= f.text_field :counter, value: "1", class: "mat-input-element", autofocus: true %>
<button type="submit" class="mat-icon-button mat-button-base mat-primary add-button" title="<%= t('AddToUsed') %>">
<span class="fa fa-plus"></span>
</button>
<% end %>
</td>
<% end %>
<td class="no-wrap"><%= skill.code %></td>
<td><%= link_to skill.translated_name, skill %></td>
<td><%= link_to translation_for(skill.parent.name_translations), skill.parent %></td>
<td><%= skill.responsible.name %></td>
<td><%= skill.updated_by %></td>
<td class="text-right"><%= format_date(skill.updated_at) %></td>
</tr>
<% end %>
</tbody>
Thanks a lot for your help!
According to this form helpers guide, the syntax you used doesn't exist
form_with model: #model, action: :custom_action
So in this case, you have to specify the url parameter for form_with to make it works.
<%= form_with model: #skill, url: :add_to_cart_skill_path(#skill), method: :post, remote: false do |f| %>

Conditional hidden tag based on checkbox condition in Rails

How can I send a hidden tag based on whether or not a checkbox is checked?
I have a table with a product title and product price and check box selection on each row and on the form submit I'd like to send both of these values to a controller. I was only able to get one value to submit with a single checkbox, so I added a hidden tag field, however, this hidden tag will submit every row, which is not what I want. In the params sent example below it should have two items and two prices sent, but it sends all the prices for each row. (As an aside, if there is a better way to send both params without using a hidden tag please let me know!)
This is data from a google analytics API report request FYI =>
#product_revenue.reports[0].data.rows
p.dimensions[0] = "Product Title"
p.metrics[0].values[0] = "Product Price"
The structure of this comes from here.
View Code:
<div class="col-md-6">
<%= form_tag add_multiple_path, method: :post do %>
<table>
<thead>
<th><strong>Product Title</strong></th>
<th><strong>Product Price</strong></th>
<th><strong>Add?</strong></th>
</thead>
<% #product_revenue.reports[0].data.rows.each do |p| %>
<tr>
<td><%= p.dimensions[0] %></td>
<td><%= p.metrics[0].values[0] %></td>
<td>
<%= check_box_tag 'price_test_datum[product_title][]', p.dimensions[0] %>
<%= hidden_field_tag('price_test_datum[product_price][]', p.metrics[0].values[0]) %>
</td>
</tr>
<% end %>
</table>
<%= submit_tag "Add selected" %>
<% end %>
</div>
The hidden field is dumping all of the column values instead of the one associated with that row?
Parameters sent:
{
"utf8"=>"✓",
"authenticity_token"=>"token here",
"price_test_datum"=>{
"product_price"=>["29.98", "14.99", "14.99", "14.99", "14.99", "14.99", "299.95", "35.97", "21.98", "10.99", "33.98", "27.98", "13.99", "59.99", "29.98", "59.98", "29.99", "110.93", "4088.79"],
"product_title"=>["Turquoise Bracelets", "Red Bracelets"]
},
"commit"=>"Add selected"
}
So I added an index to the table loop and used the checkbox value to submit the relevant row value (index) to the controller. I then used hidden tag fields to send all the product title and price values in as arrays to the controller and used the row key to find the relevant value. This seems like an inelegant solution, but it worked.
<%= form_tag add_multiple_path, method: :post do %>
<table>
<thead>
<th><strong>Product Title</strong></th>
<th><strong>Product Price</strong></th>
<th><strong>Add?</strong></th>
</thead>
<% #product_revenue.reports[0].data.rows.each_with_index do |p, index| %>
<tr>
<td><%= p.dimensions[0] %></td>
<td><%= p.metrics[0].values[0] %></td>
<td>
<%= check_box_tag 'row[]', index %>
<%= hidden_field_tag 'price_test_datum[product_title][]', p.dimensions[0] %>
<%= hidden_field_tag 'price_test_datum[product_price][]', p.metrics[0].values[0] %>
</td>
</tr>
<% end %>
</table>
Controller Code
def add_multiple
params[:row].each do |r|
PriceTestDatum.create(product_title: params[:price_test_datum][:product_title][r.to_i],
product_price: params[:price_test_datum][:product_price][r.to_i])
end
respond_to do |format|
format.html { redirect_to price_test_data_path }
format.json { head :no_content }
end
end

Controller fails to pass instance variables into view

In my home page I iterate over collections of objects, and for each object I render its attributes in a table row. There are four collections of objects, defined as instance variables in my controller, all making Guard (according to the used method) raising one of the following errors:
ActionView::Template::Error: undefined method `each_with_index' for nil:NilClass
ActionView::Template::Error: undefined method `any?' for nil:NilClass
The code in my application view raising the above errors is:
<table class="col-md-4 col-md-offset-1">
<thead>
<tr>
<th> Rank </th>
<th> Gamer </th>
<th> Points </th>
</tr>
</thead>
<tbody>
<% #atp_gamers.each_with_index do |gamer, index| %>
<tr>
<td class="index"> <%= index+1 %> </td>
<td class="gamer"> <%= gamer.name %> </td>
<td class="atppoints"> <%= gamer.atpscore %> </td>
</tr>
<% end %>
<tr class="current-user">
<td> <%= #atp_gamers.to_a.index(current_user) + 1 %> </td>
<td> <%= current_user.name %> </td>
<td> <%= current_user.atpscore %> </td>
</tr>
</tbody>
</table>
<table class="col-md-4 col-md-offset-2">
<thead>
<tr>
<th> Year </th>
<th> Champion </th>
<th> Points </th>
</tr>
</thead>
<tbody>
<% if #atp_champions.any? %>
<% #atp_champions.each do |champion| %>
<tr>
<td class="year"> <%= champion.created_at.year %> </td>
<td class="winnername"> <%= champion.name %> </td>
<td class="winnerpoints"> <%= champion.points %> </td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
The above code is part of a partial (named _gamers_home.html.erb) rendered in the original home page:
<% if logged_in? %>
<% if current_user.gamer? %>
<%= render 'static_pages/gamers_home' %>
<% else %>
<%= render 'static_pages/non_gamers_home' %>
<% end %>
<% else %>
<%= render 'static_pages/non_logged_in_home' %>
<% end %>
The logged_in? method is defined as !current_user.nil?
The instance variables that result nil are: #atp_gamers, #wta_gamers, #atp_champions and #wta_champions, defined in the controller as follows:
def home
if logged_in? && !current_user.gamer?
...l
elsif logged_in? && current_user.gamer?
#gamers = User.where(gamer: true)
#atp_gamers = #gamers.order(atpscore: :desc).limit(50)
#wta_gamers = #gamers.order(wtascore: :desc).limit(50)
#atp_champions = AtpChampion.all
#wta_champions = WtaChampion.all
...
end
end
The first instance variable raising the error (each_with_index' for nil:NilClass) is #atp_gamers. In view I tried to change it with its explicit value, that is User.where(gamer: true).order(atpscore: :desc).limit(50), and the respective code is accepted. After this change, Guard raises an error for #atp_champions.
With rails console #atp_gamers and #wta_gamers are not empty, returning 50 records out of 100 users. #atp_champions and #wta_champions are not nil, but empty arrays.
I suspect that this might be an issue raised only by Guard, because the rails server succeeds in rendering the view.
def home
if logged_in? # delete this line
...
end # delete this line
end
Delete the if logged_in?, and see what happens.
Maybe you have to use before_action :logged_in_user, only :home in controller and define the logged_in_user method as private method.
If non-logged-in users also allowed to access the home action, you need to use if statement erb in the view. Like,
<% if logged_in? %>
<% #atp_gamers.each_with_index do |gamer, index| %>
...
<% end %>
--UPDATE--
Maybe, it needs to toss variables to the partial.
Replace
<%= render 'static_pages/gamers_home' %>
to
<%= render partial: 'static_pages/gamers_home', locals: {atg_gamers: #atp_gamers, wta_gamers: #wta_gamers, atp_champions: #atp_champions, wta_champions, #wta_champions} %>
and, replace the #atp_gamers, #wta_gamers, #atp_champions, #wta_champions in the partial to atp_gamers, wta_gamers, atp_champions, wta_champions.
Try and see what happens.

Dynamic checkbox with Ajax in Rails

I have a Form with some checkboxes loaded from the Database, and an option to add other items manually to the items table. This is done by Ajax in the code below...
item_controller.rb
def manual_item_add
#manual_item = Item.find(params[:id_item].to_i)
respond_to do |format|
format.js
end
end
manual_item_add.js.erb
$("#items_table").append("<%= escape_javascript(render 'manual_item_add', :manual_item => #manual_item) %>")
_manual_item_add.html.erb
...
<td><%= check_box_tag("manual_items[]", item.id, true) %></td>
...
edit_items.html.erb
<%= form_tag( {controller:"item", action:"edit_items"}) do %>
<p align="center">
<%= select_date(#account.start_date, prefix: 'start_date') %>
to
<%= select_date(#account.end_date, prefix: 'end_date') %>
</p>
<%= hidden_field_tag 'id_account', #account.id %>
<table id="items_table" class="subtable" align="center" width="55%">
....
<tr>
<th colspan="6">Items added manually</th>
</tr>
<tr>
<th># ID</th>
<th>Type</th>
<th>Description</th>
<th>Ammount</th>
<th>Date</th>
<th>Include in the account</th>
</tr>
</table>
<p align="center"><%= submit_tag("Save", class: "btn") %></p>
<% end %>
<%= form_tag( {controller:"item", action:"manual_item_add"}, method:"get", remote: true) do %>
<h4 align="center">Add item manually</h4>
<p align="center">Item ID:
<%= text_field_tag "item_id", nil , size:5, maxlength:5 %>
<%= submit_tag("Add Item", class: "light_btn") %>
</p>
<% end %>
So... the problem here is that though I see the new checkboxes i am adding to the table (they are being created normally), the "manual_items[]" array is not being passed to the controller when I submit the resulting form (by the way, the "items_table" is inside the form definition).
Does anybody know what I am doing wrong? Sorry for the newbie question, I'm starting to work with Ruby + Rails.
Unfortunately, I don't have a definitive answer for this problem. The only working solution I've tried was to use JQuery to force parameters to be part of the form:
$(document).ready(function() {
$("#edit_items_form").submit(function(event) {
$(this).append($("input[name='manual_items[]']:checked"));
$(this).submit();
});
});
I am definitely not comfortable to this solution, I'd like to know why these Ajax checkboxes are not automatically recognized as being part the form.

undefined method nil:NilClass ruby

What I need to do is create a page where the user will type in a last name and the system will return information related to it. I keep receiving the error "undefined method `each' for nil:NilClass." I am stuck and can not debug it any help or guidance would be greatly appreciated.
CODE FOR THE INPUT PAGE
<%= form_tag(showcustcourses_custcoursesout_path, :controller => "showcustcourses", :action => "custcoursesout", :method => "post") do %>
<div class="field">
<%= label_tag :Customer_Name %><br />
<%= text_field_tag :customer_name_in %>
</div>
<div class="actions">
<%= submit_tag "Submit Customer Name" %>
</div>
<% end %>
CODE FOR THE CONTROLLER
class BookinController < ApplicationController
def bookin
end
def bookout
#customer_name = params[:customer_name_in]
#r = Customer.find_by_last(#customer_name)
#rate_list = #r ? #r.rates : nil
end
end
CODE FOR THE OUTPUT PAGE (<% #customer_list.each do |m| %> is throwing the error)
<h1>Bookin#bookout</h1>
<p>Find me in app/views/bookin/bookout.html.erb</p>
<center><table width = 65% border = 1>
<tr> <th> Customer Name</th><th> Room Number </th> <th> Cost </th></tr>
<% #customer_list.each do |m| %>
<tr> <td> <%= m.name %> </td> <td> <%= m.roomnumber %> </td> <td> <%= m.cost %> </td> </tr>
<% end %>
</table> </center><br /> <br />
You are getting the error undefined method 'each' for nil:NilClass because you forgot to set the value of instance variable #customer_list. So, #customer_list is nil.
You need to set #customer_list variable in the action corresponding to your view which in your case is bookout action as you are rendering bookout.html.erb.
Simply, do this in BookinController#bookout:
def bookout
## ...
#customer_list = Customer.all ## Add this
end
UPDATE
As per the chat discussion, OP needed to show last(from customers table), roomlevel(from apples table), cost(from rates table)
Suggested to modify
bookout method as below:
def bookout
#customer_list = Customer.all
#customer_name = params[:customer_name_in]
end
and bookout.html.erb as below:
<% #customer_list.each do |customer| %>
<% customer.bookings.each do |booking| %>
<tr>
<td> <%= customer.last %> </td>
<td> <%= booking.apple.room_level %> </td>
<td> <%= booking.apple.cost %> </td>
</tr>
<% end %>
<% end %>
Also, OP's schema was not correct to achieve this result. Added apple_id to bookings table and removed rate_id from it.
NOTE: As you don't want bookings to be associated with rates table,rate_idwas removed from bookings table. You would have to add cost field in apples table to display the cost in the view.
Add this in your controller. It will bring details of all customers.
def bookin
#customer_list = Customer.all
end
def bookout
#customer_list = Customer.all
#customer_name = params[:customer_name_in]
#r = Customer.find_by_last(#customer_name)
#rate_list = #r ? #r.rates : nil
end
If still it returns nil error, then it means you do not have any customer records in database.
<h1>Bookin#bookout</h1>
<p>Find me in app/views/bookin/bookout.html.erb</p>
<center><table width = 65% border = 1>
<tr> <th> Customer Name</th><th> Room Number </th> <th> Cost </th></tr>
#check if object is not nil
<% if !#customer_list.nil? %>
<% #customer_list.each do |m| %>
<tr>
<td> <%= m.name %> </td>
<td> <%= m.roomnumber %> </td>
<td> <%= m.cost %>
</td>
</tr>
<% end %>
<% else %>
<p> no customers available </p>
<% end %>
</table>
</center>
#customer_list is not defined
defined it in controller like this
#customer_list = Customer.all

Resources