checkbox helper sending an extra param? - ruby-on-rails

<%= form_for list do |f| %> <!-- This might be an issue in production mode -->
<%= f.fields_for l = list.list_items.build, index: l.id do |list_item| %>
<td><%= list_item.check_box :user_item_id, { checked: false, include_hidden: false }, item.id, "nil" %></td> <!-- checkboxes seem problematic, try check_box_tag? -->
<td><%= item.name %></td>
<td>$<%= number_with_precision(item.price, :precision => 2) %></td>
<td><%= list_item.number_field :quantity,
min: 1,
class: "num"%></td>
<% end %>
<% end %>
If I have three of these on my page and all three are checked, Rails will send an extra unchecked value for each form. Why is this?
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sn2HLFvVLX7ZFxBTHvRGz25H+bbyiVl8E8Kok5J7788q8WA33U8RpWmLXjU2GTjD2ybW7RMaiX1CwLJ6oCLXmQ==", "list"=>{"list_item"=>[{"user_item_id"=>"nil"}, {"user_item_id"=>"19", "quantity"=>"1"}, {"user_item_id"=>"nil"}, {"user_item_id"=>"20", "quantity"=>"2"}, {"user_item_id"=>"nil"}, {"user_item_id"=>"21", "quantity"=>"3"}], "name"=>""}, "commit"=>"Create List"}
However if I turn hidden fields off, I get other issues with my forms "stealing" each-other's data when a preceding box is left unchecked. For example here I left the first form unchecked:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"qszyk+q+pJ77WuDh2/K2S8lTuBD99DptwqeEdDI4U+4yQBWIbCSYRUvGrofzH8hHfDKXSxxn6myTpZ6dAGFruA==", "list"=>{"list_item"=>[{"quantity"=>"1", "user_item_id"=>"20"}, {"quantity"=>"2", "user_item_id"=>"21"}, {"quantity"=>"3"}], "name"=>""}, "commit"=>"Create List"}
Notice how the first unchecked form neglected to set the user_item_id to "nil", and instead left the param out completely and stole the next form's user_item_id, thus messing up my quantity values (which I set to the same as before).
Why is this?
PS: I originally had "nil" just nil, but doing so prevents the unchecked value from being passed in any case and I was having issues with the forms stealing values from each other like I posted above. If anyone could tell me how to make it so that an unchecked box means that my fields_for will not be submitted at all that would be great.

That is done by rails so that the right value is sent when the checkbox is unchecked.
When you don't check the checkbox the value of the hidden field(added by the checkbox helper) reaches your server, as unchecked checkboxes are not sent by the browsers.
In the other hand, when you check it, the last value with the same name wins, meaning the checked value will be sent to the server and everything will work as you expect.
from the documentation:
Gotcha
The HTML specification says unchecked check boxes are not successful,
and thus web browsers do not send them. Unfortunately this introduces
a gotcha: if an Invoice model has a paid flag, and in the form that
edits a paid invoice the user unchecks its check box, no paid
parameter is sent. So, any mass-assignment idiom like
#invoice.update(params[:invoice]) wouldn't update the flag.
To prevent this the helper generates an auxiliary hidden field before
the very check box. The hidden field has the same name and its
attributes mimic an unchecked check box.
This way, the client either sends only the hidden field (representing
the check box is unchecked), or both fields. Since the HTML
specification says key/value pairs have to be sent in the same order
they appear in the form, and parameters extraction gets the last
occurrence of any repeated key in the query string, that works for
ordinary forms.

Related

How to pass number_field to controller as parameter?

The problem is this: I am showing some data to the user in a table and in it has a field of the numeric type that the user fills. However, I do not know how to send the value populated by the user as a parameter to the controller.
I've tried using the following syntax:
<td><%= number_field :stock, params[:amount] %></td> ## HERE IS THE PROBLEM
<td><%= link_to 'Buy', new_transaction_path(stock: {stock_id: "#{stock[:id]}", buy_price: "#{stock[:value]}", amount: "#{params[:amount]}", transaction_type: 'buy'}), method: :post %></td>
I would like to pass the value that the user informed thought the number_field as a parameter to the controller, but it's not working..
For your case "that the user fills", You have to user a form with a submit button, Here is a relate question, If the value is determined you can use link_to otherwise a form is better, Although sometimes I rely on JS or Jquery, I give the link_to a specific id and I give the input field and id too (HTML id), Then make an action listener on the the <a> that link_to generates, Once user click on it I get the data and submit the data.
The problem for the JS way is that if you are listing a table for more than one link_to for the product with multiple numeric_field you have to make sure that the id is unique, you can simply doing this by adding _#{id} part to the generated id.

rails link_to tag to build join record in rails

I'm trying to build a form that allows a user to add products to an order.
The way I have it setup so far is that a user will select from 2 dropdown boxes and type into 1 text field
1 - the product they want
2 - its size
3 - the quantity they want.
What I hope to do is have the user click a link_to tag to "Add" this item to their order.
I was thinking I could do this via ajax and build the associative record in my controller and have it render on the page when the request returns.
When the user is done with their order and hits submit I can create my Customer Order with the products they wish to buy.
Am I approaching this correctly?
e.g. my form has the following:
<%= collection_select :order_line_item, :cake_id, Cake.order(:name), :id, :<%= grouped_collection_select :order_line_item, :cake_size_id, Cake.all, :cake_sizes, :name, :id, :name %>
<%= label_tag :quantity %>
<%= text_field_tag :quantity %>
<%= link_to "Add to order", add_to_order_path, {method: :post, remote: true} %>
Am I approaching this correctly? I then need to be able to add the fields above to the ajax post so I can populate the associative record with the relevant values.
Am I approaching this correctly?
I don't know about 'correctly'. But, I can imagine some alternatives.
Here are some sketches:
One Option:
This approach assumes that the Order is already saved so that you can associate a Product with that order. Perhaps Order has a status.
You could wrap that whole bit (product, size, quantity) in its own form (not embedded within your order form).
Have the form submit via js using remote: true (if you're using Rails 5, then this may be the default behavior).
When the user clicks on "Add", you will receive the field values as parameters in your controller where you can associate the Product with the Order.
Then, render back an HTML blob that can be inserted into the DOM (perhaps an order row?)
Use js to insert the blob and clear the form.
Another Option:
You could leave that whole bit (product, size, quantity) as not a form and have it reside outside your form.
Wrap it all up in a div.
Convert that link into a span or something similar.
Attach an .on 'click' event (I'm assuming jquery, you don't specify, so I'm going to run with it) to the wrapper.
When the link is clicked, the click event will bubble up to the wrapper.
Have the wrapper submit the field values via ajax.
Proceed as above.
I wouldn't really recommend this approach as it seems to me that you're basically replicating the functionality of a remote form. But, there is...
Yet Another Option
This approach does not require that the Order already exists.
You could have a hidden order item row outside of your form.
You construct your page as above in Another Option.
Now, when the user clicks the "Add" button, clone the hidden order item row.
Fill in the cloned order item with the appropriate values.
Insert the cloned order item into your Order form.
When the user clicks "Order" or "Submit" or whatever they click when they're done, you'll get all of the order rows as field sets.
Process the order line items along with the form. (Some folks might suggest accepts_nested_attributes_for, but I never use that.)
I suspect there are others. Or perhaps variations.

Rails nested form with a "select" form element giving me "undefined method `merge' for #<ActiveSupport::SafeBuffer:0xb50d34b8>"

Background: My goal is for a view to display a list of "condition" has_many objects, which are themselves STI subclasses of a StateDescription. I want a user to be able to pick what type of state description they are from a drop down menu (which will conditionally display a different type of form, eventually)
Inside of my main forms, I am doing a nested form like this:
<%= f.fields_for :conditions do |e| %>
<li>
<%= e.select(:type, StateDescription.subclasses.collect{|x| x.to_s}, options_for_select(StateDescription.subclassSelectForms)) %>
<br>
<%= e.label :title %>
<%= e.text_field :title %>
</li>
<% end %>
This works just fine with the text field at the bottom there (I can change values and save them, etc). But when I try to do a select statement, it explodes.
Specifically, if I don't use e.select and just do:
<%= select(:type, StateDescription.subclasses.collect{|x| x.to_s}, options_for_select(StateDescription.subclassSelectForms)) %>
it renders just fine, but doesn't actually change any values as it is not associated with a model.
If I get rid of trying to have it display a value different than it submits and just do
<%= e.select(:type, StateDescription.subclasses.collect{|x| x.to_s}) %>
Then it works just fine(I can submit, the value is saved to the database, I can retrieve it, etc).
I can LEAVE it like this, but I had wanted to have my more human readable string display instead of a rails class thing (which might look like gibberish to a non-programmer end user).
So, my question is: Why do options_for_select break when nested and associated with a form, but dont' seem to when not associated with a form? Is it just a known bug? Am I doing something wrong?
Edit:
.subclasses is a standard rails calls which returns a list of subclasses for an object.
.subclassSelect forms goes through each subclass, and turns it into a hash of the form:
{subclass.to_s => subclass.human_readable_string} and compiles all of them into an array.
I know this array of hashes works, because if I do NOT put "e" on the front, it displays correctly and works "right" for what I have done (i.e. the subclass is returned correctly based on which human readable string I select from the drop down menu, but since it's not actually associated with any form, it doesn't get set in the controller).

Rails custom form generator - radio button not in response parameters unless it has a value

Ok, I've been digging around and haven't found an answer to this. I have a pretty complex custom rails form generator application that renders pages, sections, & surveys (forms) from a database.
It does validation server side (haven't finished the javascript yet and want both types), and I have it working for all types of form input objects, except for radio buttons. Because I can't get it to submit radio buttons to show up in params when they are not checked. As opposed to just looking for radio buttons outside of the params, I'd like to find a way to check them in my response loop (if possible).
I've seen this suggestion of binding it to the model to make sure it validates, but my questions are unique and therefore I don't have a model object I'm binding it to.
My form is declared in one partial:
<%= form_tag take_surveys_path, :id => "take_surveys_new", :method => :post do %>
and my code for generating the radio button (part of a partial that looks at a question type field):
<% when 'Radio Button' %>
<% question.answers.each do |answer| %>
<%= radio_button "response[#{question.id}]content", question.question_text, answer.value %>
<%=answer.value%>
<br />
<% end %>
<% end %>
I iterate over params in my "take_survey_controller" and then check each question to see if it's valid, which includes regex and required validation based on some attributes set in my question object:
params[:response].each do |question_id, answer|
#find my questions and answers, call
if item.valid?
#do a bunch of saving and stuff...
end
end
but this never gets called for radio buttons because empty radio buttons don't post to the params.
Any suggestions or help? Happy to share more code if needed.
What you're looking for is a way to set a default value for your radio buttons. Checkboxes do this by default, but in this instance it doesn't look like it's happening for your radio buttons.
Try this:
<%= hidden_field "response[#{question.id}]content", question.question_text %>
<%= radio_button "response[#{question.id}]content", question.question_text, answer.value %>
It sounds like part of your validation should include checking input params. If a radio button isn't selected it won't submit a value, thus you need to have a list of question ids and ensure each question was submitted before moving into your params response loop.
array_of_question_ids.each do |id|
handle_missing_question(id) unless params[:response][id]
end
params[:response].each ...

In Rails, What's the Best Way to Get Autocomplete that Shows Names but Uses IDs?

I want to have a text box that the user can type in that shows an Ajax-populated list of my model's names, and then when the user selects one I want the HTML to save the model's ID, and use that when the form is submitted.
I've been poking at the auto_complete plugin that got excised in Rails 2, but it seems to have no inkling that this might be useful. There's a Railscast episode that covers using that plugin, but it doesn't touch on this topic. The comments point out that it could be an issue, and point to model_auto_completer as a possible solution, which seems to work if the viewed items are simple strings, but the inserted text includes lots of junk spaces if (as I would like to do) you include a picture into the list items, despite what the documentation says.
I could probably hack model_auto_completer into shape, and I may still end up doing so, but I am eager to find out if there are better options out there.
I rolled my own. The process is a little convoluted, but...
I just made a text_field on the form with an observer. When you start typing into the text field, the observer sends the search string and the controller returns a list of objects (maximum of 10).
The objects are then sent to render via a partial which fills out the dynamic autocomplete search results. The partial actually populates link_to_remote lines that post back to the controller again. The link_to_remote sends the id of the user selection and then some RJS cleans up the search, fills in the name in the text field, and then places the selected id into a hidden form field.
Phew... I couldn't find a plugin to do this at the time, so I rolled my own, I hope all that makes sense.
I've got a hackneyed fix for the junk spaces from the image. I added a :after_update_element => "trimSelectedItem" to the options hash of the model_auto_completer (that's the first hash of the three given). My trimSelectedItem then finds the appropriate sub-element and uses the contents of that for the element value:
function trimSelectedItem(element, value, hiddenField, modelID) {
var span = value.down('span.display-text')
console.log(span)
var text = span.innerText || span.textContent
console.log(text)
element.value = text
}
However, this then runs afoul of the :allow_free_text option, which by default changes the text back as soon as the text box loses focus if the text inside is not a "valid" item from the list. So I had to turn that off, too, by passing :allow_free_text => true into the options hash (again, the first hash). I'd really rather it remained on, though.
So my current call to create the autocompleter is:
<%= model_auto_completer(
"line_items_info[][name]", "",
"line_items_info[][id]", "",
{:url => formatted_products_path(:js),
:after_update_element => "trimSelectedItem",
:allow_free_text => true},
{:class => 'product-selector'},
{:method => 'GET', :param_name => 'q'}) %>
And the products/index.js.erb is:
<ul class='products'>
<%- for product in #products -%>
<li id="<%= dom_id(product) %>">
<%= image_tag image_product_path(product), :alt => "" %>
<span class='display-text'><%=h product.name %></span>
</li>
<%- end -%>
</ul>

Resources