ruby rails reading form parameters - ruby-on-rails

I'm trying to read parameters from html form and update some fields in the database...
Html is showing data from multiple "stories" and I want to be able to change Story.estimate field...
The html has text input fields for every story showing like so:
<%= text_field_tag story.id, story.estimate, :class => "input-small" %>
My idea was to name these input fields by the ID of their story and then read and update them in the controller like so:
#story.update_attribute("estimate",params[#story.id])
But this of course does not work... I need some help... There has to be a better, simpler way of doing this...

You should try
<%= text_field_tag "story[#{story.id}]", story.estimate, :class => "input-small" %>
and in your controller
you will get the params like this
if params[:story].present?
id = params[:story].keys.to_i
value = params[:story].values.first
#story = Story.where(id: id).first
#and then finally update the story
#story.update_attribute("estimate",value)
OR
#this will also update your story for the corresponding story id
Story.where(id: id).update_all(estimate: value)
end

Also, I believe update_attribute may have been removed in rails 4 in favor of update_column. So if you are running rails 4 that may be an issue. (https://github.com/rails/rails/commit/a7f4b0a1231bf3c65db2ad4066da78c3da5ffb01)

Related

Rails only pass params that have changed on edit submit

I have a edit form that prepopulates with the current values. Its a custom edit screen (not the default one that rails uses) and what Im using it for is for users to submit changes that will get voted on and might eventually get applied to the record. However, in the time it takes to be voted on something else might have changed and I dont want to overwrite the changes if they didnt submit a change.
EDIT: Changing to my more specific case so hopefully answers will work for it...
I have the following tables: Recipes, RecipeIngredients, RecipeSteps, RecipeChanges. On the show view of my recipes it displays all the ingredients/steps and there is a tab that then changes just the ingredients/steps to forms as to allow the user to submit changes. I dont want these changes applied though. Instead Im trying to create a voting system where people can vote on them. So what I have decided on is to convert the parameters from the form into a json string and save it in the RecipeChanges table under a single column (instead of using two table for ingredient changes and step changes). Heres the code for the form (html removed to make it easier to see the rails stuff):
<%= form_for #recipe, url: recipe_recipe_changes_path(#recipe), html: {method: "post"}, remote: true do |f| %>
<%= f.fields_for :recipe_ingredients, f.object.recipe_ingredients.order(:order) do |ff| %>
<%= ff.hidden_field :order, class: "position" %>
<%= ff.text_field :ingredient, placeholder: "Add Ingredient (e.g. 3 cups cooked rice)" %>
<label><%= ff.check_box :_destroy %>Remove</label>
<% end %>
<%= f.fields_for :recipe_steps do |ff| %>
<%= ff.hidden_field :order, class: "position"%>
<%= ff.text_area :step %>
<label><%= ff.check_box :_destroy %>Remove</label>
<% end %>
<%= submit_tag "Submit", class: "button" %>
<% end %>
So this sends a recipe object to my RecipeChange controller and there I handle the params to save them as the json string like so:
def create
#change = RecipeChange.new
#change.recipe_id = params[:recipe_id]
#change.new_recipe = recipe_change_params.to_json
#if #change.save
#add alert for successfully adding
#else
# add code for error handling
#end
end
This works like I want except for it saves all the ingredients/steps and I would like to only save what they have changed. I had two thoughts on how to do this but not sure how to accomplish it.
Check if the fields have changed when they click the submit button and only send the ones that have been edited (not sure if possible)
In the controller grab the original recipe (I have the id so that would be easy) and loop through the ingredients/steps and compare them and remove any that are identical....this is the method I think would be better but not sure how to loop through the hashes to accomplish this
Have a look at ActiveModel::Dirty. http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-changed
You can do something like:
changes = bag.changed_attributes and get a hash of that attributes that changed, and then save those with bag.update_attributes(changes), for example.
This is a bit old now but I've come across the same or similar scenario and wanted to share for others.
In my case I populate some nested form fields based on an existing object in my #new action. However, in my #create action I did not want to save these nested form params unless they were actually modified compared to the original existing object.
In this case, ActiveModel::Dirty would always be true as it would compare [nil, "value"].
I first tried to modify the params in my #create action and compare them to the original existing object similar to this discussion but this got messy and felt wrong.
I ended up saving all records then doing a cleanup with an instance method in my model that I call after save in my controller's #create action. Still feels a bit dirty but it's working.
Example:
# controllers/changes_controller.rb
# ChangeController#create
def create
# ... shortened for example ...
if #instance.save
#instance.remove_clean_changes
format.html
end
end
# models/change.rb
# Change#remove_clean_changes
# Loop over all original objects and compare the necessary attributes
# to the changes. If they match, they are clean and should be deleted.
def remove_clean_changes
original_objects = self.original_objects
changes = self.changes
original_objects.each do |original_object|
changes.each do |change|
change.destroy if (change.attribute_one == original_object.attribute_one &&
change.original_object_id == original_object.id)
end
end
end

how form_for works in Ruby on Rails

I am an newbie. I have read the API documentation. But still don't understand how form_for works.
Firstly, from Ruby on Rails Tutorial, the form for follow button:
<%= form_for(current_user.relationships.build(followed_id: #user.id)) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
I understand current_user.relationships.build(followed_id: #user.id) means a new record. But why can we not just submit and trigger controller to save the record without hidden_field? Why do we still need to post followed_id to controller?
Secondly, in hidden_field, what does :followed_id means? I believe that is a symbol, i.e. it equals only "followed_id" not a variable of id. If that is only the name of the input field, then what is its value?
Thirdly, how does form_for know where the submission should be sent to? Which controller and action the form_for will post to?
Fourth, how does params work with form_for? In this follow button case, params[:relationship][:followed_id] will return #user.id in controller. How does it know the first hash attribute is :relationship? We have neither mentioned form_for :relationship nor form_for #relationship.
I know these questions can be very dumb, but I am really stuck. Any help will be appreciated.
I didnt do that tutorial so mind me if i dont answer directly to your question.
Take a look at the rails guide about form helpers and it explains in details your questions, probably in a more articulate way than i can.
form_for(path/to/your/controller/action) is a helper method to create HTML form elements with the url path to the POST or GET request. The helper knows if it should be a new record or an update record based on what you are asking to do in your controller action.
For example
In your controller
def new
#my_instance_variable = Myobject.new
end
In your view new.html.erb
<%= form_for #my_instance_variable do |f| %>
...
<% end %>
In your case the logic was directly written in the helper and you could also directly write
<%= form_for Myobject.new %>
Both will result with the following html
<form action="/myobjects/new" method="post">
# in this case rails knows its a `POST` request because the route new action
# is by default a POST request. You can check these routes and their request
# by using `rake routes` in terminal.
Then the hidden_field is another helper to contain a value, in your case the #user.id that will be passed as parameter then saved as a Create or update action for the given object. The reason it doesnt add the value in the hidden field tag is because you already have a model association that knows the id of user since the link of form uses the build method with user id.
Last part you need to understand the form_for link logic
current_user.relationships
# implies the association of the current_user has many relationships
current_user.relationships.build
# .build is a method to populate a new object that can be save as a new record
# means you will create a new relationship record by populating the user_id
# column with the current_user.id and the followed_id with the target #user.id
After reading the book The Rails 4 Way, I understand form_for better now.
11.9.1.5 Displaying Existing Values.
If you were editing an existing instance of Person, that object’s attribute values would have been filled into
the form.
in this way, when we build the relationship by usingcurrent_user.relationships.build(followed_id: #user.id), the relationship instance will be created and gain attribute followed_id. So that, instead of "creating" a relationship, we are actually editing the relationship by the form.
Then Rails will know you are editing and load the existing attribute "followed_id" to the field. Therefore, we don't need to assign value to the field like using f.hidden_field :followed_id, value: #user.id.
And the reason why we have to use a field to pass followed_id to params is because HTTP server is stateless, it doesn't remember you are creating a relationship with which user.
One of the advantages of writing form_for current_user.relationships.build(followed_id: #user.id) instead of standard form_for #relationship is we don't need to write "if-condition" in controller like this:
unless current_user.nil?
if current_user.following?(#user)
#relationship=current_user.relationships.find_by(followed_id: #user.id)
else
#relationship=current_user.relationships.new
end
end
params will be sent to the controller which belongs to the instance's model. "post" method will go to action create, "delete" will go to destroy, "patch" will go to update, etc.
params will be a hash with another hash inside like { instace_name: { field_1: value1, field_2:value2 } } or full params as below
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"afl+6u3J/2meoHtve69q+tD9gPc3/QUsHCqPh85Z4WU=",
"person"=>{"first_name"=>"William", "last_name"=>"Smith"},
"commit"=>"Create"}

How to get an option from select box in rails controller?

I want to ask that how can i get the selected option in my controller action in rails, so that i can save it in database. I don't want to use rails helper for select-box and options. As i have copied the select box code, and it has large number of options. I hope my question is clear enough.
I am using form_for helper though.
Regards.
You'll have code like this:
form_for #variable do |f|
f.select --------------
end
You'll want to catch the params & save them, like this:
def new
#variable = Model.new
end
def create
#variable = Model.new(post_params)
#variable.save
end
private
def post_params
params.require(:variable).permit(:select_name)
end
Just realized you're using Rails 3. My code is for Rails 4, but the process remains the same -- treat the select box as any other input, and to do that you have to ensure it's called from the form_for function
If you want to create a country select box, you could use the country_select gem with this code in your form_for:
<%= f.country_select :country, ["United States"] %> #Can write any default country
This will keep it consistent with Rails

ruby on rails search form

I'm new to RoR and I've managed to make a basic search form but keep getting errors when trying to expand the search tags (name).. I have a model with various data (location, website, email, telephone) and was wondering how I can add these to my current search code.
/models/ciir.rb
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
static_pages_controller.rb
def home
#ciirs = Ciir.search(params[:search])
end
/home.html.erb
<%= form_tag ciirs_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag " Search Database Records ", :name => nil %>
</p>
<% end %>
When clicking the submit button (no search terms) the url is:
ciirs?utf8=✓&search=
but when modifying the name condition to something like 'website' the url changes to
ciirs?utf8=✓&search=&commit=+Search+Database+Records+ –
Since you mentioned you are new to RoR, I must share the way I learned RoR was reading, using and analyzing one issue at a time. I would suggest you to take a look at following points one at a time and try & learn how RoR treats them and how these fit your question:
How form_tag works?
How text_field_tag works?
Once you have understood form_tag, difference between text_field_tag and f.text_field?
How params objects are created, and it uses names of form controls?
How and when to use GET and/or POST form methods? Inadvertently, what are different types of method and when to use them?
How URL are used in the form_tag and what components are they made of?
Sprinkle a bit of knowledge of Ruby language by learning between Arrays and Hashes? In fact, learn Ruby as much as you can.
Answering your question,
/home.html.erb
<%= form_tag "/static_pages/home", :method => 'post' do %>
<p>
<%= text_field_tag "search[name]", params.has_key?("search") && params[:search].has_key?("name") ? params[:search][:name] : "" %>
<%= submit_tag " Search Database Records " %>
</p>
<% end %>
/models/ciir.rb
def self.search(search)
if search
find(:all, :conditions => ["name LIKE '%?%'", search[:name]])
else
find(:all)
end
end
So I modified your form, and told RoR about search params containing data for name.
params is a Hash (which is a key-value pair) having key named search, which further is a Hash having key named name.
The same principle is followed in the model code. We passed the Hash of key search to the function and in there, used the value of key named name.
I also updated the url in form_tag, to point it to home action of your controller. Assuming that you have added it to your routes.rb file, it usually follows the pattern controller_name/action_name or the function name action_name_controller_name_path or action_name_controller_name_url. Run rake routes command at your root directory to list out all paths in your application.
Also note, I used POST method instead of original GET. You may wish to use GET here, so please change it back.
I hope this works.
I found no error in your code. the url changed to ciirs?utf8=✓&search=&commit=+Search+Database+Records+ is normal. submit_tag generates a button named "commit" defaultly, it will be parsed in the params. I see you add :name => nil , it will fix the problem, the other part of your code needn't to be modified. I copied your code and tested it, it ran smoothly.

ruby on rails form_for

I have a calendar_date_select in a view that shows a table listing all the information on a certain phone. I want to add a To: and From: date range that a user can select and update the table in the view. The structure is like this:
Usage Controller
Detail action in the usage controller that shows the call history from a certain phone.
Inside detail I want the To and from fields with a refresh button.
What is exactly happening in this code:
<% form_for :date_range do |f| %>
<%= f.calendar_date_select :start, :time => true %>
<%= f.calendar_date_select :end, :time => true %>
<%= f.submit "Submit" %>
<% end %>
Does this pass a hash to the usage controller and look for a date_range method? My current route looks like this
usage/detail/id-of-phone
I would like it to look something like this:
usage/detail/id-of-phone#start-end
So I could then (I think) extract the start and end dates from the params with just params[:start] and params[:end]. Am I doing this right, or is there a better way to get the desired result that I want.
I haven't used the calendar_date_select plugin, but you should be getting the parameters back already.
params[:date_range][:start]
params[:date_range][:end]
What you want is the url or the smart solution to get the params?
Please set the routes.rb for the url. Or you can make many method in the 'DataRange' model.
As many programmers using, save many dates in the model. But making us terrible is using the params smartly.
Such as
class Model
def start
......
end
def end
......
end
end
You can't get the params by params[:start] if you pass the params by the form. You can see the form's html for detail.
Please use the
params[:...][:start]

Resources