I have an a column called access and it's set to array: true. I'm having trouble submitting a form with this however. Here is my controller:
Controller
def create
#topic = Topic.find_by(slug: params[:topic_id])
#navigation_item = NavigationItem.create(navigation_item_params)
if #navigation_item.persisted?
redirect_to topic_path(params[:topic_id])
else
render :new
end
end
private
def navigation_item_params
params.require(:navigation_item).permit(
:title,
:url,
:thumbnail_id,
:access,
:category_id,
:tag
)
end
Pretty standard stuff in the controller
Form
<%= form.select :access, options_for_select(custodian_profiles), { include_blank: true, multiple: true }, class: "form-control #{error_class}" %>
I also have a presence true validation on the field.
So, when I try to submit the form the access field comes back as an empty array it looks like this #=> access: [] which obviously fails the validation check and doesn't work. How do I accept multiple select values with rails?
Looks like you may need another pair of brackets, try this:
<%= f.select :access, options_for_select(custodian_profiles), {}, {:multiple => true, :class => "form-control #{error_class}", include_blank: true } %>
Also make sure that your custodian_profiles variable is returning an array. Hope that helps.
Related
In a form, I want to include blank only in case of Client.count>1. Is there a clean way of doing this?
Now I'm using this select:
= f.select :client_id, Client.all.map{|c| [c.full_name, c.id]}, {include_blank: true}
You can use a tiny decorator:
class ClientDecorator
def self.form_select_choices
Client.pluck(:full_name, :id)
end
def self.form_select_include_blank?
{ include_blank: Client.count.positive? }
end
end
So, in your view you call those class methods:
<%= form.select :client_id, ClientDecorator.form_select_choices, ClientDecorator.form_select_include_blank? %>
Now you can test that and leave the database interaction far from the views.
I have a helper with us state names and abbreviations and am successfully using this form in an edit profile view to render the states in a dropdown and save a state to the db when the user selects and submits the form.
<%= f.label :State %>
<%= f.select(:state, options_for_select(us_states), :include_blank => "Please Select") %>
Once my form saves and reloads, even though the state saved to the db, the form still shows "Please Select", so I have tried this:
<%= f.label :State %>
<%= f.select(:state, options_for_select(us_states), selected: #clinician_profile.state) %>
But then it shows the first state in the helper array (Alabama) rather than the saved state.
How can I have it both show "Please Select" when no value is in the db but show the appropriate state once the user has already selected it?
Thanks for any help!
Here is the very top of my form:
<%= form_for(#clinician_profile) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
Here is my controller:
class ClinicianProfilesController < ApplicationController
def edit
#clinician_profile = current_user.clinician_profile
end
def index
end
def show
end
def create
end
def update
#clinician_profile = current_user.clinician_profile
if #clinician_profile.update_attributes(clinician_profile_params)
flash[:success] = "Profile Updated"
redirect_to edit_clinician_profile_path(current_user)
else
render 'edit'
end
end
private
def clinician_profile_params
params.require(:clinician_profile).permit(:address1, :address2, :city, :state, :zip,
:accepting_patients, :rate, :license_number, :license_state, :years_licensed, :years_practicing,
:school, :year_graduated, :accepts_insurance, :sliding_scale, :bio, :website, insurance_ids: [], race_ids: [], language_ids: [])
end
end
The problem is when using options_for_select with select, the selected option should be included as an argument for options_for_select. Also you should use prompt instead of include_blank So your code should look like below
<%= f.select(:state, options_for_select(us_states, selected: #clinician_profile.state), prompt: "Please Select") %>
options_for_select takes two arguments (https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select) where the second argument is nil by default.
You need to pass the value of the state selected as a second argument of options_for_select(us_states, selected: #clinician_profile.state.
I'm trying to create an User register form that has to accept a set of terms and condition, but I don't know how to use the value of the checkbox to verify if it was clicked(server side).
I've tried to use this http://guides.rubyonrails.org/active_record_validations.html#acceptance, but so far I haven't achieved anything.
I have validates :terms_of_service, acceptance: { accept: '1' } in my User.rb and in my form <%= f.check_box :terms_of_service,:value =>'0',:class =>"hidden", :id =>"t_and_c" %>, but I can submit the form without clicking on it. What am I doing wrong? If I have to post anything else to make the question easier to understand let me know.
In your form the check box should return true when they click it.
In your form you just specify if the checkbox is checked or not:
<%= f.check_box :terms_of_service %> Accept Terms of Services
check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
In your controller you just save what is returned from the form:
#you whitelist the parameter together with all the other params you have
def user_params
params.require(:user).permit(:terms_of_services)
end
#You create your user as usual
def create
#user = User.new(user_params)
end
In you migration, I would add a default option for :terms_of_services to make it more explicit.
class AddTermsOfServicesToUser < ActiveRecord::Migration
def change
add_column :users, :terms_of_services, :boolean, default: false
end
end
EDIT
If you don't create a column in your User model, you don't have a way to work with terms of services on the server side. You CAN work with terms_of_servies on your client side only if you like without creating a column. This is how you could do that:
Create a checkbox without the Rails check_box helper (because it would require an object that you don't have since you have no column in the database)
<input type="checkbox" id="cbox1" value="accepted" onchange='enableSubmitTag(this)'>
<label for="cbox1">Accept Terms of Services</label>
Disable the Submit button by default
<%= f.submit "Create", class: "btn btn-primary", id: "submit_button", disabled: true%>
When they click the terms of services checkbox, enable the submit button again.
function enableSubmitTag(element) {
if(element.checked){
document.getElementById('submit_button').disabled = false;
} else {
document.getElementById('submit_button').disabled = true;
};
};
If you store :terms_of_services in your table, you can validate it on your server side too. You can use all the javascript. You just have to change the checkbox:
<%= f.check_box :terms_of_service, :onchange => 'enableSubmitTag(this)' %> Accept Terms of Services
Model should have:
validates :terms_of_service, acceptance: true
Form should have:
<%= form_for :user, url: users_path do |f| %>
...
<%= f.label :terms_of_service %><br>
<%= f.check_box :terms_of_service %>
...
<% end %>
and Rails should do everything else for you.
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
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...