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.
Related
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
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} %>");
I have a form_tag with a radio_button_tag and it populates with data from DB. It must simply be directed to a customised update action(update_multiple) where a boolean column is updated for all those 2 records which have been changed in the form.
For e.g. say when initially the form was populated from DB with record 1's radio button selected and now user changed his selection to record 3, then at Submit of form tag the update of both records must occur but the problem is the code at submit, only collects id of record which is now selected in that group.How do I get id of that record also which was unselected so that I can update_all for both of them at one go?
And if Submit cannot handle this action, then is there a way in controller or form to persist the id of the initial selected record before populating the form? As you see, I've tried with collecting an array of ids[] with radio_button_tag.
TIA for your help.
Here's the form code:
<%= form_tag update_multiple_user_cv_attachments_path, method: :put, action: :update_multiple do %>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th> Select a CV </th>
<th> Resume Name </th>
</tr>
</thead>
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<%= hidden_field_tag cv_attachment.main, :value => params[:main] %>
<tr>
<td><%= radio_button_tag "cv_attachment_ids[]", cv_attachment.id, cv_attachment.main %> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= submit_tag "Select Main", :class =>'button' %>
<% end %>
Here's the controller update_multiple code.
def update_multiple
CvAttachment.update_all(["updated_at=?", Time.now], :id => params[:cv_attachment_ids])
end
I can think of 2 ways to achieve your objective.
update the boolean for all the attachments which belong to the user to false and then update the one which has been selected to true
include a hidden field in the form and set it to the id that is already true. Then in the controller action, update the one that is selected to true and the one in the hidden field to false. This is probably a better option and you'll probably want to wrap the d/b updates in a transaction.
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<% if cv_attachment.main %>
<%= hidden_field_tag "ex_main_cv", cv_attachment.id %>
<% end %>
<tr>
<td><%= radio_button_tag "main_cv", cv_attachment.id, cv_attachment.main %> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
controller
def update_main_attachment // probably a better name for this method
if params["ex_main_cv"] != params["main_cv"]
Attachment.transaction do
deselected_attachment = Attachment.find(params["ex_main_cv"]
deselected_attachment.update_attribute(:main, false)
selected_attachment = Attachment.find(params["main_cv"]
selected_attachment.update_attribute(:main, true)
end
end
end
Many thanks #margo. Here' how I resolved it partly your way of using hidden_field. But for now keeping this thread open as I'm making 2 DB updates for toggle of same column.
<tbody>
<% #cv_attachments.each do |cv_attachment| %>
<% if cv_attachment.main %>
<%= hidden_field_tag "ex_main", cv_attachment.id %>
<% end %>
<tr>
<td><%= radio_button_tag "new_main", cv_attachment.id, cv_attachment.main, :id => "#{cv_attachment.id}"%> </td>
<td><%= cv_attachment.attachment.file.basename %></td>
</tr>
<% end %>
</tbody>
and in controller:
def update_main
if request.put?
if params["ex_main"] != params["new_main"]
CvAttachment.find(params[:ex_main]).toggle!(:main)
CvAttachment.find(params[:new_main]).toggle!(:main)
end
end
I'm looping through each instance of a built sub-tournament - and the problem that I'm having has to do with conditionally creating a collection_select box with data fetched via ajax. Here's the view - the line I want to insert code in is marked:
View
<% #tournament.sub_tournaments.each_with_index do |sub, i| %>
<%= f.fields_for :sub_tournaments, sub, :validate => false do |sub_form| %>
<div class="tab-content standings-row-<%= i %>" style="display:none">
<table>
<thead>
<tr>
<th> <h4>Standing</h4> </th>
<th class="standings-field-<%= i %>"></th>
<th></th>
</tr>
</thead>
<tbody>
<%= sub_form.fields_for :standings, :validate => false do |standings| %>
<tr>
<td>
<%= f.hidden_field :_destroy %><%= f.text_field :standing, :class => "standing", readonly: true, :type => "" %>
</td>
<td class="standings-ajax-<%= i %>">**INSERT HERE**</td>
<td><span class="remove">Remove</span></td>
</tr>
<% end %>
</tbody>
</table>
<div class="add-item">
<%= link_to_add_standings_fields(sub_form, :standings) %>
</div>
</div>
<% end %>
<% end %>
I thought about doing the conditional check (it depends upon whether the game selected is a team game or a single-player game) in the controller, but it seems to make more sense as a method (or a helper?). At the moment I have it in Standing.rb (below) - but I'm getting a no method "collection_select" error - so probably form helpers aren't available in models, which seems reasonable. So how could I do this?
Standing.rb
def team_or_player(game)
if Game::TEAM_GAMES.include?(game.name)
self.collection_select(:team_division_id, TeamDivision.where("game_id = ?", game.id),
:id, :name, {include_blank: true})
else
self.collection_select(:player_id, Player.where("game_id = ?", game.id),
:id, :handle, {include_blank: true})
end
end
And how can I pass the f to my AJAX call?
AJAX
$(".standings-ajax-<%= #tab_number %>").html("<%= ** ?? **.team_or_player(#standing, #game) %>");
You can call helper in model:
ActionController::Base.helpers.collection_select( ... )
So from what I can see you should change team_or_player() to class method and call it with:
Standings.team_or_player(#standing, #game)
or as instance
#standing.team_or_player(#standing, #game)
But that you should use self instead of passing #standing.
Me preference would be to put that logic directly in view or to helper.
I have a simple select form. When a year is selected from the form I would like the results to be returned to the same page but below the form. Can someone explain how to do this? Here is the form page (index.html.erb)
<%= form_tag("lookup/show", :method => "get") do %>
<%= label_tag(:q, "Pub Year :") %>
<%= collection_select(:lookup, :pubyear, #pubyears, :pubyear, :pubyear) %>
<%= submit_tag("Find") %>
<% end %>
Here is the show method from the Lookup controller
def show
#lookuprows = Lookup.return_lookup_row(params[:lookup][pubyear])
respond_to do |format|
format.html
end
end
Here is the show.html.erb page that the results currently go to
<tbody class="lkuptbody">
<% #lookuprows.each do |lkup| %>
<tr class="lkuprow">
<td><input type="text" class="lkupcode" value=<%= lkup.codetype %> /></td>
<td><input type="text" class="lkupdesc" value=<%= lkup.codedesc %> /></td>
<td><input type="text" class="lkuprmks" value=<%= lkup.rermark %> /></td>
</tr>
</tbody>
I understand that I will have to make a partial _show.html.erb, but how do I reference that from the form page (index.html.erb)?
Thanks
If you want the results to appear on the same page but below the form, then the form should send the results to the index action, not to the show action:
<%= form_tag("lookup", :method => "get") do %>
<%= label_tag(:q, "Pub Year :") %>
<%= collection_select(:lookup, :pubyear, #pubyears, :pubyear, :pubyear) %>
<%= submit_tag("Find") %>
<% end %>
and in your LookupController:
def index
#lookuprows = Lookup.return_lookup_row(params[:lookup][pubyear]) unless params[:lookup].nil?
...
end
Then just append the table HTML in your show page below the form (in index.html.erb) wrapped in an if block to filter out the case where #lookuprows is nil:
<% if #lookuprows %>
<tbody class="lkuptbody">
<% #lookuprows.each do |lkup| %>
<tr class="lkuprow">
<td><input type="text" class="lkupcode" value=<%= lkup.codetype %> /></td>
<td><input type="text" class="lkupdesc" value=<%= lkup.codedesc %> /></td>
<td><input type="text" class="lkuprmks" value=<%= lkup.rermark %> /></td>
</tr>
<% end %>
</tbody>
<% end %>
This will show the results in #lookuprows as a table if there are any, if not it will not show the table.
You may want to put that table HTML in a separate partial to clean up the view, but that is not essential to the problem you asked.
Hope that helps.
You want to avoid reloading the page and put it under the form? Here's how to do it with ajax :
Index.html.erb.
<%= form_tag("lookup/show", :method => "get", :data => {:remote => true, :type => "html"}, :id => "lookup_form") do %>
<%= collection_select(:lookup, :pubyear, #pubyears, :pubyear, :pubyear, id: 'lookup_show_select') %>
<%# Add More fields here %>
<%= submit_tag "Submit" %>
<% end %>
<div id='lookup_show_result'></div>
In app/assets/lookup.js.coffee. If you don't use coffeescript, it's easy to translate to JS, just ask.
$ ->
$(document).on 'ajax:success', '#lookup_form', (evt, data) ->
$('#lookup_show_result').html(data)
Controller unchanged, just process the request according to all parameters
This is a minimal untested version, tell me if you're stuck
On a side note, I'm starting to develop a gem that will extend UJS implementation of Rails with jQuery here to make standard update/delete etc easier with ajax:
jQuery UJS extended