I am building a rails application using ActiveAdmin, and want to build a form for a custom action in the controller. I am trying to pass #listings from a collective_action to the rendered file, allowing me to edit multiple records at once. The #listings are based on an ActiveRecord query which draws record IDs from the URL.
It seems to be successfully accessing the parameters from the URL, and querying the database. When the form is served to the browser however, it is not able to produce the values of listing.title. Any ideas?
Here is my Listing.rb:
ActiveAdmin.register Listing do
collection_action :batch_upload do
ids = params[:id]
#listings = []
#listings = Listing.find(ids)
render template: 'listings/edit_batch'
end
end
Here is my edit_batch.html.haml:
= semantic_form_for :listing, :url => {:controller => 'listings', :action => 'batch_upload'}, :html=>{:method=>:put} do |f|
-#listings.each do |listing|
=f.hidden_field :id, :value => listing.id
=f.input :title, :value => listing.title
=f.submit "Submit"
If the form is correctly displaying listing.id but not listing.title then I suspect the record does not have title set, or listing does not have a title attribute.
To check, run the Rails console and find the record using the id from the form:
$ Listing.find(1)
Check the returned object to see whether it is missing the title.
I changed the code to the input so that it accesses its html directly and it worked:
=f.input :title, :input_html => { :value => listing.title }
Using Formtastic's inputs block might help simplify the inputs for each listing. Doing so will allow the form to create attribute fields for each Listing object.
# edit_batch.html.haml
= semantic_form_for :listing, :url => {:controller => 'listings', :action => 'batch_upload'}, :html=>{:method=>:put} do |f|
- #listings.each_with_index do |listing, index|
f.inputs for: listing, for_options: { index: index } do |l|
= l.input :id, as: :hidden
= l.input :title
= f.submit "Submit"
The :for and :for_options scope inputs fields to a specific object.
Related
I am making an API call but not saving all of the data to my database, so I assumed that I do not need to make a model for that purpose. However, I do not know how to generate a form for this API call, which is in my controller.
Here is the form in my view:
<%= simple_form_for ITHINKINEEDSOMETHINGHERE url: searchApis_path, :method => 'get' do |f| %>
<%= f.input :keyword, :placeholder => 'keyword', input_html: { name: :keyword } %>
<%= f.input :city, :placeholder => 'city', input_html: { name: :keyword } %>
<%= f.input :start_date, :placeholder => 'YYYY-MM-DD', input_html: { name: :start_date } %>
<%= f.input :end_date, :placeholder => 'YYYY-MM-DD', input_html: { name: :end_date } %>
<%= f.button :submit, "Submit" %>
<% end %>
My corresponding controller:
class HomeController < ApplicationController
def index
end
def searchApis
start_date = params[:start_date]
end_date = params[:start_date]
keyword = params[:keyword]
city = params[:city]
eventbrite_request = Typhoeus::Request.new('https://www.eventbriteapi.com/v3/events/search?q='+keyword+'&sort_by=best&venue.city='+city+'&start_date.range_start='+start_date+'T00:00:00Z&start_date.range_end='+end_date+'T00:00:00Z',
method: :get,
headers: { 'Authorization' => ENV['EVENTBRITE']})
#response = eventbrite_request.run
# yelp_request = Typhoeus::Request.new('',
# )
# set #result to be the data that I want.
end
end
I am getting an "undefined method 'model_name'" error.
The route which I am making the GET request to is /searchApis, so I am guessing that the url in the form should be searchApis_path.
So far I have mainly learned how to make a form to generate a new instance of a model, but in this case, the form is to essentially initiate the API call, whose response I will later display under my form. For what it's worth, I want to later be able to save select data from the response JSON into a 'bookmarks' model.
Thanks.
Where you wrote ITHINKINEEDSOMETHINGHERE is where the record, typically an ActiveRecord model, usually goes. It is used in the construction of the various field names.
Since it looks like you're doing a search controller, you could put :search there, which then would make all your form fields named things like search[keyword]
On your backend then, you just have to access the proper params object, which is typically nested under whatever you named the record, so in this case, params[:search][:keyword].
http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for
I know you're using simple_form, but it inherits a lot from form_for, so that page is still a good reference
Im trying to design a shopping cart. i.e a customer shopping online adds a product to their trolley.
I want to go straight to create action from my new action without going to new.html.erb with pre-set values in my params
Here is what I have so far:
#trolley_id += 1
redirect_to :controller => 'trolleys', :action => 'create', :id => #trolley_id, :something => 'else', method: :post
This redirects me to my index action
To do this with javascript templates, it would look like this:
view
= form_form Trolley.new, remote: true do
-# form goes here
The remote true will submit it as javascript, which will try to render a javascript template.
Your create action can either render :create or let Rails render your template automatically. Since it came in as a javascript request, Rails will render the template with format js.
trolleys/create.js.erb
var html = "<%= j render 'trolley_row', trolley: #trolley %>
$('table.trolleys body').append(html);
I managed to resolve my problem. I created a form in my Product_controller#show that will go straight to my Trolley_controller#create and create an entry in my Trolleys table
<%= simple_form_for [#product, #trolley] do |f| %>
<%= f.input :quantity, collection: 1..12, prompt: "select quantity" %>
<%= f.input :product_id, :as => :hidden %>
<%= f.input :user_id, :as => :hidden %>
<%= f.button :submit, "Add to Basket" %>
<% end %>
Here is the relevant code from views/products/edit.html.erb:
<%= form_for(:product, :url => {:action => 'update', :id => #product.id}) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<%= submit_tag("Update Product") %>
<% end %>
from views/products/_form.html.erb:
<%= select_with_new_option(f, :shop, :name, :id) %>
and from helpers/products_helper.rb:
def select_options_with_create_new(objects, text_field, value_field, options={})
object = objects.to_s.singularize
all_objects = object.capitalize.constantize.all
all_objects = all_objects.sort_by(&options[:order_by]) if options.has_key?(:order_by)
select_options = all_objects.map{ |obj| [obj.send(text_field), obj.send(value_field)] }
select_options << [wrap_create_new_option("create new #{object}".titleize), "new_#{object}"]
options_for_select(select_options)
end
def wrap_create_new_option(str)
">> #{str} <<"
end
# By default, sorts by 'text_field'.
def select_with_new_option(f, object, text_field, value_field)
f.select(:"#{object}_id", select_options_with_create_new(object.pluralize, text_field, value_field, :order_by => text_field))
end
I expect the select box to be #product.shop_id by default, but this is not true (the first option is always the default value).
What am I missing ?
Alright, I got it. Just remove options_for_select(select_options) in the select_options_with_create_new method.
The options_for_select assembles the 2-d array select_options into a string of html options, which is accepted by the select form helper, however, without assigning the selected attribute. Just pass the 2-d array to the select method as the second argument, it would assign the selected automatically.
I think that, in this case, all you need to do is make sure you set #product in your edit action:
def edit
#product = Product.find(params[:id])
end
Then change your first line:
<%= form_for(:product, :url => {:action => 'update', :id => #product.id}) do |f| %>
to
<%= form_for(#product, :url => {:action => 'update', :id => #product.id}) do |f| %>
The first code block will certainly update the proper product, but you'll only see the current values reflected if you generate a form_for #product, with that #product variable set.
I have a data model in my Rails project that has a serialized field:
class Widget < ActiveRecord::Base
serialize :options
end
The options field can have variable data info. For example, here is the options field for one record from the fixtures file:
options:
query_id: 2
axis_y: 'percent'
axis_x: 'text'
units: '%'
css_class: 'occupancy'
dom_hook: '#average-occupancy-by-day'
table_scale: 1
My question is what is the proper way to let a user edit this info in a standard form view?
If you just use a simple text area field for the options field, you would just get a yaml dump representation and that data would just be sent back as a string.
What is the best/proper way to edit a serialized hash field like this in Rails?
If you know what the option keys are going to be in advance, you can declare special getters and setters for them like so:
class Widget < ActiveRecord::Base
serialize :options
def self.serialized_attr_accessor(*args)
args.each do |method_name|
eval "
def #{method_name}
(self.options || {})[:#{method_name}]
end
def #{method_name}=(value)
self.options ||= {}
self.options[:#{method_name}] = value
end
attr_accessible :#{method_name}
"
end
end
serialized_attr_accessor :query_id, :axis_y, :axis_x, :units
end
The nice thing about this is that it exposes the components of the options array as attributes, which allows you to use the Rails form helpers like so:
#haml
- form_for #widget do |f|
= f.text_field :axis_y
= f.text_field :axis_x
= f.text_field :unit
Well, I had the same problem, and tried not to over-engineer it. The problem is, that although you can pass the serialized hash to fields_for, the fields for function will think, it is an option hash (and not your object), and set the form object to nil. This means, that although you can edit the values, they will not appear after editing. It might be a bug or unexpected behavior of rails and maybe fixed in the future.
However, for now, it is quite easy to get it working (though it took me the whole morning to figure out).
You can leave you model as is and in the view you need to give fields for the object as an open struct. That will properly set the record object (so f2.object will return your options) and secondly it lets the text_field builder access the value from your object/params.
Since I included " || {}", it will work with new/create forms, too.
= form_for #widget do |f|
= f.fields_for :options, OpenStruct.new(f.object.options || {}) do |f2|
= f2.text_field :axis_y
= f2.text_field :axis_x
= f2.text_field :unit
Have a great day
emh is almost there. I would think that Rails would return the values to the form fields but it does not. So you can just put it in there manually in the ":value =>" parameter for each field. It doesn't look slick, but it works.
Here it is from top to bottom:
class Widget < ActiveRecord::Base
serialize :options, Hash
end
<%= form_for :widget, #widget, :url => {:action => "update"}, :html => {:method => :put} do |f| %>
<%= f.error_messages %>
<%= f.fields_for :options do |o| %>
<%= o.text_field :axis_x, :size => 10, :value => #widget.options["axis_x"] %>
<%= o.text_field :axis_y, :size => 10, :value => #widget.options["axis_y"] %>
<% end %>
<% end %>
Any field you add in the "fields_for" will show up in the serialized hash. You can add or remove fields at will. They will be passed as attributes to the "options" hash and stored as YAML.
I've been struggling with a very similar problem. The solutions I found here were very helpful to me. Thank you #austinfromboston, #Christian-Butske, #sbzoom, and everyone else. However, I think these answers might be slightly out-of-date. Here's what worked for me with Rails 5 and ruby 2.3:
In the form:
<%= f.label :options %>
<%= f.fields_for :options do |o| %>
<%= o.label :axis_y %>
<%= o.text_field :axis_y %>
<%= o.label :axis_x %>
<%= o.text_field :axis_x %>
...
<% end %>
and then in the controller I had to update the strong parameters like so:
def widget_params
params.require(:widget).permit(:any, :regular, :parameters, :options => [:axis_y, :axis_x, ...])
end
It seems to be important that the serialized hash parameter comes at the end of the list of parameters. Otherwise, Rails will expect the next parameter to also be a serialized hash.
In the view I used some simple if/then logic to only display the hash if it is not empty and then to only display key/value pairs where the value was not nil.
I was facing the same issue, after some research i found a solution using Rails' store_accessor to make keys of a serialized column accessible as attributes.
With this we can access "nested" attributes of a serialized column …
# post.rb
class Post < ApplicationRecord
serialize :options
store_accessor :options, :value1, :value2, :value3
end
# set / get values
post = Post.new
post.value1 = "foo"
post.value1
#=> "foo"
post.options['value1']
#=> "foo"
# strong parameters in posts_controller.rb
params.require(:post).permit(:value1, :value2, :value3)
# form.html.erb
<%= form_with model: #post, local: true do |f| %>
<%= f.label :value1 %>
<%= f.text_field :value1 %>
# …
<% end %>
No need setter/getters, I just defined in the model:
serialize :content_hash, Hash
Then in the view, I do (with simple_form, but similar with vanilla Rails):
= f.simple_fields_for :content_hash do |chf|
- #model_instance.content_hash.each_pair do |k,v|
=chf.input k.to_sym, :as => :string, :input_html => {:value => v}
My last issue is how to let the user add a new key/value pair.
I will suggest something simple, because all the time, when user will save form You will get string. So You can use for example before filter and parse those data like that:
before_save do
widget.options = YAML.parse(widget.options).to_ruby
end
of course You should add validation if this is correct YAML.
But it should works.
I'm trying to do something similar and I found this sort of works:
<%= form_for #search do |f| %>
<%= f.fields_for :params, #search.params do |p| %>
<%= p.select "property_id", [[ "All", 0 ]] + PropertyType.all.collect { |pt| [ pt.value, pt.id ] } %>
<%= p.text_field :min_square_footage, :size => 10, :placeholder => "Min" %>
<%= p.text_field :max_square_footage, :size => 10, :placeholder => "Max" %>
<% end %>
<% end %>
except that the form fields aren't populated when the form is rendered. when the form is submitted the values come through just fine and i can do:
#search = Search.new(params[:search])
so its "half" working...
I have a form field where a user enters contact information, including name, email, etc. If they already have contacts saved I want to have a dropdown with their saved contacts. When a contact from the dropdown is selected, the contact fields should be populated with that data to allow for easy editing.
For some reason this approach isn't working:
In my new.html.erb view:
<%= f.collection_select :id, #contacts, :id, :name, :onchange =>
remote_function(:url =>{:action => 'populate_contact_form'}, :with => 'id') %>
In my controller:
def populate_contact_form
raise "I am working up to this point"
#contact = current_account.contacts.find(params[:id])
end
In populate_contact_form.rjs:
page['contact_name'].value = #contact.name
page['contact_email'].value = #contact.email
It seems my controller method is never called... can anyone explain how to do this?
It's not getting called because you're using remote_function incorrectly. Rails assumes the current action/controller/id/etc if any of those options are missing from the :url option to remote_function. You're passing :action as a top level option to remote_function, and it gets ignored. With a :url option Rails assumes the same action and controller that rendered this view.
This should fix your problem:
<%= f.collection_select :id, #contacts, :id, :name, :onchange =>
remote_function(:url =>{:action => 'populate_contact_form'}, :with => 'id') %>