ruby on rails form_for - ruby-on-rails

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]

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

Saving custom attribute to Order model in Spree eCommerce

I have added a custom field in my spree_orders table (let's call it custom_attribute).
I have added Spree::PermittedAttributes.checkout_attributes << [:custom_attribute] to my spree.rb initializer.
In my checkout process I have a custom form with the following code (html formatting has been removed):
<%= form_for #order do |alt_form| %>
<%= alt_form.label :custom_attribute, "Custom Attribute" %><span class="required">*</span><br />
<%= alt_form.text_field :custom_attribute, :class => 'form-control required', maxlength: 11 %>
<% end %>
This form successfully submits the field in the post request (full dump below) to http://localhost:3000/checkout/update/address as order[custom_attribute] xyz, however, the information is not saved to the model.
_method=patch
_method=patch
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
commit=Save and Continue
order[bill_address_attributes][address1]=123 Test
order[bill_address_attributes][address2]=
order[bill_address_attributes][city]=Test
order[bill_address_attributes][country_id]=232
order[bill_address_attributes][firstname]=Test
order[bill_address_attributes][id]=3
order[bill_address_attributes][lastname]=Test
order[bill_address_attributes][phone]=555555555
order[bill_address_attributes][state_id]=3535
order[bill_address_attributes][zipcode]=30024
order[email]=spree#example.com
order[custom_attribute]=2414
order[state_lock_version]=32
utf8=✓
utf8=✓
I've inserted #order.inspect on the following (payment) page to can see at that point that #order.custom_attribute is still nil.
Does anyone have any idea about what I need to do in order to get the custom_attribute value sent in the post request saved to the model with the other attributes sent?
-------------------edit-------------------
Default spree permitted attributes are defined here https://github.com/spree/spree/blob/3-0-stable/core/lib/spree/core/controller_helpers/strong_parameters.rb and are added on by the strong_paramaters helper here (don't have the rep to post a third link):
module Spree
module Core
module ControllerHelpers
module StrongParameters
def permitted_attributes
Spree::PermittedAttributes
end
delegate *Spree::PermittedAttributes::ATTRIBUTES,
to: :permitted_attributes,
prefix: :permitted
def permitted_payment_attributes
permitted_attributes.payment_attributes + [
source_attributes: permitted_source_attributes
]
end
def permitted_checkout_attributes
permitted_attributes.checkout_attributes + [
bill_address_attributes: permitted_address_attributes,
ship_address_attributes: permitted_address_attributes,
payments_attributes: permitted_payment_attributes,
shipments_attributes: permitted_shipment_attributes
]
end
def permitted_order_attributes
permitted_checkout_attributes + [
line_items_attributes: permitted_line_item_attributes
]
end
def permitted_product_attributes
permitted_attributes.product_attributes + [
product_properties_attributes: permitted_product_properties_attributes
]
end
end
end
end
end
which can be at found spree/core/lib/spree/core/controller_helpers/strong_parameters.rb in the spree github repo.
-------------------final edit-------------------
If anyone finds this in the future and is trying to troubleshoot a similar issue, my code above is actually correct; I had (stupidly) placed it in an if Rails.env.production? block.
I will give you an example, maybe you can translate it into your code.
OPTIONAL
Imagine that I have a custom action, called "custom" on my users controller, defined this way in my routes:
resources :users do
collection do
get 'custom'
post 'custom'
end
end
This way I can call it by using custom_users_path.
Next, I want a form that submits to that function, to do that you need to specify an additional parameter in your form_for called :url, in this example I call it using custom_users_path, once I submit the form, It will run my custom action.
form_for would look like this:
<%= form_for :user, :url => custom_users_path do |f| %>
<%= f.text_field :random %>
<%= f.submit "Submit" %>
<% end %>
Then, I want to be able to access some :random parameter in my users controller. Let's suppose that I have a text_field which I want store the value on my :random parameter (see above). First, you need to permit that parameter to be accessible in your controller, in this example, in users controller. This way:
params.require(:user).permit(YOUR PARAMETER HERE, {:random => []})
So, every time I submit the form, I can access the :submit parameter value, by doing this params["controller-name"]["parameter-name"], translated into this example, would look like:
params["user"]["random"]
You can then convert it into string using to_s if you want.
Output (Supposing that I wrote "444" on my text_field):
444
I hope this helps you.

Rails Undefined Method Path for New

I am relatively new to rails but having a real problem with something that I know should be really simple. I have a model called channel, in it I have a simple new method, in the view I have form but every time I try and load it, I get an error to say:
undefined method `channels_path'
My view (new.html.erb) is really simple, for the minute it just has a button in it with a name and a value, it just looks like this:
<%= simple_form_for #channel do |f| %>
<%= f.error_notification %>
<%= f.button :submit, 'Free Plan', name: 'plan', value: 'free' %>
<% end %>
My Controller has:
def new
#channel = Channel.new
end
And in my routes I have:
resources :channel
Output form a rake routes is:
channel_index GET /channel(.:format) channel#index
POST /channel(.:format) channel#create
new_channel GET /channel/new(.:format) channel#new
edit_channel GET /channel/:id/edit(.:format) channel#edit
channel GET /channel/:id(.:format) channel#show
PATCH /channel/:id(.:format) channel#update
PUT /channel/:id(.:format) channel#update
DELETE /channel/:id(.:format) channel#destroy
Which all looks how I expect. But as the error says there is no channels_path, but as far as I am aware, there shouldn't be.
I am sure this is supposed to be really simple but I just cannot see what I am doing wrong. Can anybody help?
Many thanks
David
EDIT
I have updated the route to be:
resources :channels
I can now load the form, however I now get the error when trying to submit it:
param is missing or the value is empty: channel
Being caused by:
# only allow specific params
def channel_params
params.require(:channel).permit(:name,
:slug,
:description,
:plan,
:subscription_ends
)
end
I am assuming singular is correct here based on the model, but have tried plural too with no luck. Any more thoughts?
Many thanks
Edit
Got it working in the end, it appears you have to have at least one input in your form. I added an input for the name field and it started working.
Many thanks to everyone that commented
According to your rake task, the path should be
channel_path
If it's not working with the simple_form_for helper, it's probably because you should have set up your routes as resources: channels
UPDATE
The new bug is coming from nothing being received by the controller for :channel
Try adding a field like so
f.hidden_field :plan, :value => "free"

setting a dynamic link_to rails

I'm pretty new to rails and I'd like to set my links for a certain page dynamically. I have a table called "Unfinished" and it has a column called "link" (corrected from "links") I'd like to be able to call the "link" record in the view to set my link_to link path.
I am trying to do this...
<%= link_to #unfinished.link(:p => #post.id) do %> FINISH <% end %>
...but that's not working.
my controller says:
def show
#post = Post.find(params[:id])
#unfinished = Unfinished.where('progress = ?', #post.progress).last
end
and the controller logic works fine...until I try to put the #unfinished.link into link_to
Edit:
Error Message:
wrong number of arguments (1 for 0)
Model
class Unfinished < ActiveRecord::Base
end
The type of links are :
step1_path
step2_path
step3_path
I am making a multipage form that you can save partway through. Based on a value in the #post.progress column (like 1, 2, 3) the correct path to complete the post will be provided (step1_path, step2_path etc...)
try this.
<%= link_to eval(#unfinished.link.to_s) do %> FINISH <% end %>
since the link you want is actually a named route, so you will need to eval it.
but with this you wouldn't be able to be able to pass in the post id, which you will need.
If the route is the same for all records (save for what part you are on based on the progress attribute) do you even need to store it in the database? You could just make the link method return the path (that you would still need to eval).
something like
def link (post)
"step#{self.progress}_path(post.id)"
end
and then eval the link on the way back. but Not sure if that will work, just thinking out loud...
There are gems that do multi-stage forms perhaps looking into them might help?

ransack search form in header partial: No Ransack::Search object was provided to search_form_for

First of all, I'm new to RoR, so the answer may be obvious, in which case I apologize. I've looked around and haven't found anything that helps.
I'm trying to have a search form at the header of every web page on my app that will search through the names of all my "buckets". Here is the relevant code:
In app/views/layouts/_header.html.erb (within a nav bar):
<% search_form_for #q do |f| %>
<%= f.label :name_cont %>
<%= f.text_field :name_cont %>
<%= f.submit %>
<% end %>
In app/controllers/buckets_controller.rb:
def index
unless params[:q].blank?
#q = Bucket.search(params[:q])
#buckets = #q.result.paginate(:page => params[:page])
else
#buckets = Bucket.find(:all, :limit => 5).paginate(:page => params[:page])
end
end
I understand the last part isn't that great: what I'm trying to do is if I'm just accessing the bucket index page (not by searching), i display the 5 most recently created buckets. When I search for something in the header form, I access the index page but only show the buckets that hit the search. (would a better way to handle it to have a search page separate from my index page?)
I found this issue which is pretty much identical, but I still don't see how I handle #q if every page is going to have the form on it--surely I don't have to alter every controller's every action?
Sorry in advance for any frustration my noobishness my cause you!
As others have said, you need to utilize the ApplicationController's before_filter. Though ernie himself seems not to recommend this, the implementation is simple.
First, use the advanced Ransack options to set your path for your search thusly
#config/routes.rb
resources :buckets do
collection do
match 'search' => 'buckets#search', via: [:get, :post], as: :search
end
end
Second, update your BucketsController to include the following custom action:
#controllers/buckets_controller.rb
def search
index
render :index
end
Nothing yet out of the ordinary. If you currently try to search you will get the error from your original question. Your definition of the variable q is correctly implemented, but you will have to move it to the ApplicationController like so:
#controllers/application_controller.rb
before_filter :set_global_search_variable
def set_global_search_variable
#q = Bucket.search(params[:q])
end
Finally, update your search form to pass in the correct search options
#layouts/_header.html.erb
<% search_form_for #q, url: search_buckets_path, html: { method: :post } do |f| %>
<%= f.label :name_cont %>
<%= f.text_field :name_cont %>
<%= f.submit %>
<% end %>
No, you do not need to edit all your controllers.
You can use ApplicationController for all your "common" controller needs. Read up on it in the guides http://guides.rubyonrails.org/action_controller_overview.html and the API docs http://api.rubyonrails.org/classes/ActionController/Base.html
The key here is, when you generated your new rails app, you'll notice it created the file .../app/controllers/action_controller.rb and that class derives from ActionController::Base. Then, if you again use the rails generator to create a controller for your app, you'll notice your new controller class derives from ApplicationController (not ::Base). That means that the application_controller.rb is the parent controller class for your app. That means everything in it is available to all your app controllers. It's easy to abuse, so be judicious.
Looks like this is not possible. This is a comment from Ernie the gem author.
You'd have to handle the Ransack-required stuff in a before_filter or
(ick) in the view partial itself. If you're putting a search field on
every single part of the site, I'd recommend you strongly consider
whether ransack is the right tool for the job, as well. You might want
some sort of inverted index search setup like sphinx, solr, etc.
https://github.com/ernie/ransack/issues/3

Resources