Rails 4 collection_check_boxes persistent after form refresh - ruby-on-rails

I'm using Rails 4 collection_check_boxes in my forms. Filling out the form, I check some of the checkboxes. I've noticed that when the form refreshes after validation error, the checkboxes that were checked persists. Is this a feature of the tag? I couldn't find this information in the docs.
Checkbox form field code:
<div class="field">
<%= f.label "Area of Interest" %><br />
<%= f.collection_check_boxes :interest_ids, Interest.all, :id, :name do |b| %>
<div class="collection-check-box">
<%= b.check_box %>
<%= b.label %>
</div>
<% end %>
</div>
I do want the checkboxes to stay checked after form refresh but wanted to make sure it is a feature and not just a coincidence that is it working for me.
Any information would be helpful, thanks!

It's a feature of the tag as long as you use render :action instead of redirect_to :action to render your form upon a failed save/validation:
def create
#user = User.create(user_params)
if #user.valid?
redirect_to action: :show
else
render :new # #user gets passed to form_for
end
end
The key distinction is that when you use render :new, the #user model instance from your create action is passed to your form.
Now, in the new.html.erb view:
form_for #user do |f|
# Fields using the syntax f.text_field :attr_name, `f.collection_check_boxes :attr_name`, etc will reference the :attr_name in both #user to populate the value(s). Also, #user.errors[:attr_name] to show an error message, if present.
end
Basically, what's happening is in your controller you're calling one of save, create, validate, or valid? on your model. A failed validation after calling one of these methods prevents a save to the database, but the failed values are still present in the #user object. Also, the errors object is now populated with information on which attributes failed to updated and the reason why the validation failed.
Thus, when you re-render your form, you see the check boxes are still selected because they're populated from the values in model instance itself. Similarly any fields with matching errors should show an error for that field as well.

I don't think that the validation fail page refresh is the same action as a 'form refresh' unless you added language in your controller that would reset your form if the form fails to save.
When you check interest_ids the form and hit 'submit', it adds any checked value that passes validation into your model as a saved :interest_id value, so that saved value is what makes the checkboxes persist even if the whole form fails validation.
If you want to reset your form if any part of the form fails validation, I would suggest adding an if/else statement into your controller on the create action. #object.interest_ids = [] will reset the stored interest_ids on your object to an empty array, which will uncheck the boxes.
def create
#object = Object.new
if #object.save
redirect_to object_path(#object)
else
#object.interest_ids = []
render :new
end
end

Related

Rails form_tag not displaying the existing field value

I've a settings controller setup like this:
class Admin::SettingsController < ApplicationController
def index
#settings = Setting.all
end
def update
setting_params.each do |key, value|
Setting.where(key: key).first.update_attribute :value, value
end
redirect_to admin_settings_path, notice: "Settings saved."
end
private
def setting_params
params.require(:settings).permit(:site_title, :site_desc)
end
end
The index action has a view file in relevant path, its code is as follows:
<h1>Settings</h1>
<%= form_tag admin_settings_path, method: "put" do %>
<p>
<label>Site Title:</label>
<%= text_field_tag "settings[site_title]" %>
</p>
<p>
<label>Site Description:</label>
<%= text_field_tag "settings[site_desc]" %>
</p>
<p>
<%= submit_tag "Save settings" %>
</p>
<% end %>
Now, I'm able to save/update these fields data in relevant model and I can see the data through command Setting.all in rails console. But the issue is, after saving the record via form when I reload the settings index page the form is blank and it is not showing previously saved values in the fields.
What I'm doing wrong here?
You can do what has been mentioned in the previous answer i.e. using a form_for. However, this can also be accomplished by creating a Hash in both the controller actions that creates and processes this form.
In the action that creates this form, the values in the hash could be initialized to blanks/zeros depending upon the input tag and in the action that processes this form, the values in the hash could be assigned to the values obtained from the posted params[].
I ran into the similar issue with form_tag and I used the following code in my index action i.e. the action that renders the form initially:
#search = {name: "", ranking_group: 0}
Whereas, in the search action, I used the following code to fill up this hash:
#search = {name: params[:name], ranking_group: params[:ranking_group]}
Finally, in the view, I used the appropriate default value options with the input tags as below:
<%= text_field_tag :name, #search[:name] %>
<%= select_tag :ranking_group, options_for_select(AmenityEntities::Amenity.ranking_group_options, #search[:ranking_group]), include_blank: true %>
Hope this helps.
I think you need to use form_for(#variable) instead of just a form_tag because you need to persiste the object after save.
form_for works with resources, take a look here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html

Show selected radio buttons in edit view

I am having trouble displaying selected radio buttons in the edit action.
The app allows the user to create his own forms(surveys) and then apply them (answer them) to their children.
The issue:
When rendering the new action that allows the user to answer his form, forms display well, and save to database properly. Assigns the choices to the answer content.
On the other hand, edit action duplicates the choices, showing the ones that were selected AND new ones. I check the id of the answers and is rendering 2 times each answer.
Any ideas of how to fix this behaviour?
Relevant code:
_form.html.erb
<%= f.fields_for :answers do |a| %>
<% choices.each do |choice| %>
<%= a.radio_button :a_content, choice.c_description %>
<%= a.label :a_content, choice.c_description, :value => choice.c_description, class: 'no-margin' %>
<% end %>
<%= a.hidden_field :question_id, :value => question.id %>
<% end %>
answered_forms_controller.rb
def new
#child = current_user.children.find(params[:child_id])
#form = current_user.forms.find(params[:form_id])
#answered_form = #child.answered_forms.new(form_id: params[:form_id])
#answered_form.answers.build
end
def create
#answered_form = AnsweredForm.create(answered_form_params)
if #answered_form.save
flash[:success] = "Nuevo cuestionario " + #answered_form.form.f_title + " aplicado!"
redirect_to current_user.children.find(params[:child_id])
else
render 'new'
end
end
def edit
#child = current_user.children.find(params[:child_id])
#form = current_user.forms.find(params[:form_id])
end
def update
if #answered_form.update_attributes(answered_form_params)
flash[:success] = "Cuestionario para paciente actualizado!"
redirect_to #answered_form.child
else
render 'edit'
end
end
UPDATE:
I now figured out that the duplication occurs because in the new action I got #answered_form.answers.build, but if I remove that, I don't see the fields when creating a new answered_form.
I knew this because tried to put 2 times #answered_form.answers.build in the new action and then got duplicated when creating and triplicated when editing, so edit always adds the fields one more time than new action if you have something like #answered_form.answers.build in the new action.
I believe you should be able to do something like:
<%= f.collection_radio_buttons :a_content, choice, :id, :c_description, {}, { checked: choices.selected_response_id } %>
With this you should be able to generate your radio buttons for all your answers.
PS: the final hash has the condition for checking the selected option choices.selected_response_id you should change that to the selected response on your answer model.
Although Rails tries to infer the preselected radio button, it may not always work hence the reason for this final hash.
Read more here
If you want to preselect your radio button, it means you have already stored a boolean value in your database for this field. Just insert this value after the checked option inside your helper:
radio_button_tag("something", something, checked = value)
should work the same with f.radio_button

Rails 4 Form - Adding a new input to an existing record

I have a form where users enter profile info which is saved in the User model. I need to collect another piece of information from users at a later stage (on a different page). How do I collect this info and append it to the existing user record?
Here is my form code with the one additional input I need from the user. When I hit submit, it runs without error but the new field is not saved in my model. I ran the migration to add the field to the model.
<%= form_for #user, html: { method: :put }) do |f| %>
<div class="form-group">
<%= f.label :what_is_your_favorite_color %>
<%= f.text_field :color, class:"form-control" %>
</div>
<div class="form-group">
<%= f.submit "Submit", class:"btn btn-primary" %>
</div>
<% end %>
My controller update method is currently blank .. can you tell me what the update method should look like? The user is logged in so I need to find that user record (probably by id column?) and write or update the new input into the model.
def update
??
end
You first need to fetch your record, the pass it the params hash, and Rails will do the rest.
def update
#record = Record.find(params[:id])
if #record.update(record_params)
redirect_to #record
else
render :edit
end
end
If you are using Rails 4, you need to account for strong_parameters. So add the new attribute to the permitted attributes.
def record_params
params.require(:record).permit(:color, :and, :other, :attributes, :go, :here)
end
The above code assumes that the record id will be in the params hash, or in other words, you are using RESTful routes. If you are not, you may want to pass in the id from the session (if this is, as you say, a user record, and you are using Devise).
def update
#record = Record.find(current_user)
# ... the rest should be the same
end

conditionally exclude fields from rails form

The functionality I want to achieve with this is to have different fields based on the action the user is currently on.
The generic scaffold bundles the form into a partial and renders it in the new and edit actions. In a typical signup case, one may not want to update the password everytime the profile is updated. One way I used to solve this in the past is to create separate forms for new and edit; including the password in new only. Obviously, most fields repeat themselves. Is there a way to keep the partial but omit (thereby not updating) some fields on the form per action?
Let me assume you are building the form for user model.So in controller you will have
def new
#user = User.new
.....
end
def edit
#user = User.find(params[:id])
....
end
In the form partial use
<% if #user.new_record? %>
<%= f.field_type :field_name %>
<% end %>
This will not render the field during edit/update.
You can try it this way in one partial:
<% if params[:action] == "new" %>
<%= f.field_type :field_name %>
<% end %>
But you should think about the security because this doesnt keep away attackers from adding POST fields manually to write to fields that aret displayed as Fields on you form!
You can check whether the active record object being passed to the view (such as #user) is a new record or and old one using the method 'new_record?'. Based on this, you can decide what fields you want to display.
Alternatively, you could also have a partial and then pass it some value (most likely a Boolean) and based on that value you can decide which fields to render.
This can been done like:
render :partial => 'partial_name', :locals => {:bool => true}
And then in your partial do
<% if bool == true %>
<%= f.xyz_tag :name %> //whatever field you want
<% end %>

form_for using :symbol not working, error [Only get, put, and delete requests are allowed]

When submitting a form to create a new object i get an error message when submitting the form. When i use the same form but then with an instance variable everything seems to go fine, any clue why the submit with the :symbol fails?
The error message says: Only get, put, and delete requests are allowed.
The code for the new form with :symbol is:
<% form_for :ecard do |f| %>
<%= label(:ecard, :title) %><br/>
<%= f.text_field :title, :tabindex => "1" %>
<%= f.submit "Create Ecard" %>
<% end %>
The form goes ok when i use
<% form_for #ecard do |f| %>
<%= label(:ecard, :title) %><br/>
<%= f.text_field :title, :tabindex => "1" %>
<%= f.submit "Create Ecard" %>
<% end %>
Some code out of my controller:
# GET new_ecard_url
# return an HTML form for describing the new ecard
def new
#ecard = Ecard.new
end
# POST ecard_url
def create
# create an ecard
#ecard = Ecard.new(params[:ecard])
if #ecard.save
flash[:notice] = "Succesfully created a new Ecard"
redirect_to :action => 'index'
else
flash[:warning] = "Error when saving Ecard"
render :action => 'new'
end
end
If the first argument is a symbol, it describes the object the form is about and name the instance variable, and then you need to provide the URL.
The actual object can be used and then it will use your routes to try and determine the URL. This means that it must know of a new_ecard_path and edit_ecard_path.
It will look at the object and see if it is a new record to determine which to use.
If you are using resource routes with the default restful routes, then you can probably just use the instance object. If you need to specify the URL that the form goes to, then use the symbol and specify the URL.
There are a few examples at http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
A good way to see what's going on is to use Firebug and inspect the generated HTML. If you don't have Firebug, get it now, it's really invaluable.
If you create your form using the instance variable, you'll get something along the lines of:
<form id="new_ecard" class="new_ecard" method="post" action="/ecards">
So this will create a POST request to the /ecards action, which is the create method (by the way, your comment above the create method should be POST ecards_url, not ecard_url, unless you've defined it otherwise).
However if you only use the :ecard symbol instead of the instance variable, you'll get:
<form method="post" action="/ecards/new">
Since you haven't specified a URL, it uses the current one. This means your form will call itself in this case, and nothing will happen.
All this is due to all the so called magic Rails does - convention over configuration. But as danivo said, you can specify the URL manually and explicitly state each parameter for the form if you do not want to have this magic happen for you.

Resources