RoR 3.2.8: Is there an HTML5 combobox helper? - ruby-on-rails

Right now, I'm using the form_for.select and options_for_select rails helpers to create a select box with the data from the model. However, what I really need is a combobox like the one introduced with HTML5:
<input type=text list=browsers >
<datalist id=browsers >
<option> Google
<option> IE9
</datalist>
Is there a rails helper for creating such elements?

No, but it's quite easy to setup your own form builder helper method to achieve such a result, a simple example would be:
app/form_builders/combobox_form_builder.rb
class ComboboxFormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Context # for nested content_tag
include ActionView::Helpers::FormTagHelper #for sanitize_to_id method access
def combobox_tag(name, options, opts= {})
#template.content_tag(:input, :name => name, :id => sanitize_to_id(name), :type => "text", :list => opts[:list_id]) do
content_tag(:datalist, :id => opts[:list_id]) {options}
end
end
end
After restarting your server you can implement your new combobox using the form builder by specifying a builder argument in your form_for call:
<%= form_for #foo, builder: ComboboxFormBuilder do |f| %>
<%= f.combobox_tag(:browser, options_for_select(["Firefox", "Chrome", "IE9"]), :list_id => "list")%>
<% end %>
Output HTML:
<input type="text" name="browser" list="list" id="browser">
<datalist id="list">
<option value="Firefox">Firefox</option>
<option value="Chrome">Chrome</option>
<option value="IE9">IE9</option>
</datalist>
Keep in mind that both IE & Safari do not offer support for the HTML5 Datalist.

<%= form_for #person do |f| %>
<%= f.label :first_name, "First Name" %>:
<%= f.text_field :first_name, list: 'first-name' %>
<datalist id="first-name">
<% Person.all.each do |person| %>
<option value="<%= person.first_name %>"></option>
<% end %>
</datalist>
<%= f.submit %>
<% end %>
You may also want to do distinct:
<% Person.select(:first_name).distinct.each do |person| %>

Just example from my code:
= form_tag controller:'beer', action: 'create' do
= text_field :beer, :name, list: 'beer-name'
%datalist#beer-name
- Beer.all.each do |beer|
%option{value: beer.name}

Related

Rails Form Not Updating the Attributes

Is this the correct way of defining form, let me know if I need to provide any more details.
This is the UserPreference forms in new.html.erb
<%= form_for :user_preference, url: user_preferences_path do |u|%>
<p>
<%= u.label :title %><br>
<%= u.text_field :title %>
</p>
<p>
<%= u.label :description %><br>
<%= u.text_field :description %>
</p>
<p> <%= u.label :back_ground_color %><br>
<select name="bgcolor" id="bgcolor">
<option value="#FF3300">Orange</option>
<option value="#00FF00">Green</option>
<option value="#0000FF">Blue</option>
<option value="#FF0066">Pink</option>
<option value="#FFFF00">Yellow</option>
<option value="#FFFFFF">White</option>
</select>
</p>
<p>
<%= u.label :font %><br>
<select name="font" id="font">
<option value="Times New Roman">Times new Roman</option>
<option value="Verdana">Verdana</option>
<option value="Arial">Arial</option>
<option value="sans-serif">serif</option>
</select>
</p>
<br >
<p>
<%= u.submit %>
</p>
I am getting title and description when I am trying to render in html,
the attribute is not getting updated in database.
UserPreference.controller.rb
class UserPreferencesController < ApplicationController
def new
#user_preference = UserPreference.new
end
def create
#user_preference = UserPreference.new(userp_params)
#user_preference.save unless user_signed_in?
render plain: params[:user_preference].inspect
end
def edit
end
def update
end
private
def userp_params
params.require(:user_preference).permit(:title, :bgcolor, :font, :description)
end
end
When you say this
<%= u.text_field :description %>
because you are working with :user_preference, it will make an input like
<input type="text" name="user_preference[description]" value="foo">
(where 'foo' is the current value, or maybe it's blank)
Note the "name" attribute: this will go into params like
params = {:user_preference => {:description => "foo"}}
If you are going to hand-code the select then you need to make sure the name attribute has this structure too, eg
<select name="user_preference[bgcolor]" id="bgcolor">
So you will get params like
params = {:user_preference => {:description => "foo", :bgcolor => "#FFFFFF"}}
Which then allows you to say
#user_preference.attributes = params[:user_preference]
which is the standard way to deal with this in the controller.
However, rather than writing out all the html for the select, it's much nicer to use the rails form helpers (select in this case) like you do with the text fields. You can also define the options for the select with the options_for_select helper, which saves a lot of typing too.
<%= u.select :bgcolor, options_for_select([["Orange", "#FF3300"], ["Green", "#00FF00"], ["Blue", "#0000FF"], ["Pink", "#FF0066"], ["Yellow", "#FFFF00"], ["White", "#FFFFFF"]]) %>
It's also cleaner to define this variable of options in your code somewhere, eg in a UserPreference class method:
class UserPreference < ActiveRecord::Base
#class methods section
class << self
def bgcolor_options
[["Orange", "#FF3300"], ["Green", "#00FF00"], ["Blue", "#0000FF"], ["Pink", "#FF0066"], ["Yellow", "#FFFF00"], ["White", "#FFFFFF"]]
end
end
end
Now you can use the select like so:
<%= u.select :bgcolor, options_for_select(UserPreference.bgcolor_options) %>
http://guides.rubyonrails.org/form_helpers.html#the-select-and-option-tags

Rails 4: text_field for acts_as_taggable_on not separating tags with a comma

I am trying to get the text_field in my form partial to comma-separate acts_as_taggable_on tags. Right now, when I reload the page, the commas disappear so if a field has two or more tags, they become one big tag instead. For instance, I get "Tag1 Tag2 Tag3" instead of "Tag1, Tag2, Tag3". I am using acts-as-taggable-on 3.4.2.
Here is my _form.html.erb partial:
<h2>Tags:</h2>
<p>Please separate the tags with a comma ','</p>
<% #article.tag_types.each do |tag| %>
<div class="form-group">
<strong><%= label_tag tag.to_s.titleize %></strong><br />
<%= f.text_field "#{tag.to_s.singularize}_list".to_sym, :placeholder => "Comma-separated list of #{tag.to_s}", class: 'form-control' %>
</div>
<% end %>
Every time I reload the edit page, the input value somehow removes the commas from the already-present tags, so the text field looks like this:
<input id="article_country_list" class="form-control" type="text" name="article[country_list]" value="China U.S.A." placeholder="Comma-separated list of countries">
instead of having value="China, U.S.A." as it should be.
Here is my model, article.rb:
class Article < ActiveRecord::Base
acts_as_taggable_on :people, :cities, :countries, :other
end
Any help would be much appreciated :)
Thanks!
Apparently this is a new security feature.
I solved the comma separation issue by doing:
<% #article.tag_types.each do |tag| %>
<div class="form-group">
<strong><%= f.label tag.to_s.titleize %></strong><br />
<% tag_sym = "#{tag.to_s.singularize}_list".to_sym %>
<% tag_list = "#{tag.to_s.singularize}_list" %>
<%= f.text_field tag_sym, value: #article.send(tag_list).to_s, :placeholder => "Comma-separated list of #{tag.to_s}", class: 'form-control' %>
</div>
<% end %>
Thanks! Since I am using ActiveAdmin with Formtastic I made a custom input.
So I created a new class: app/inputs/tag_list_input.rb with:
class TagListInput < Formtastic::Inputs::StringInput
def input_html_options
super.merge(:value => "#{#object.send(method).to_s.html_safe}")
end
end
and using this like:
f.input :some_tag_list, :as => :tag_list, :label => "SomeTags"

nested params not being submitted with form

I have a nested form that is based off of the example in this rails cast:
http://railscasts.com/episodes/196-nested-model-form-revised
For my application, I'm trying to let people upload multiple files in a nested form. Each step can have many design files, and steps accept nested attributes for design files:
Step.rb:
class Step < ActiveRecord::Base
attr_accessible :design_files_attributes
has_many :design_files
accepts_nested_attributes_for :design_files, :allow_destroy => :true
...
end
DesignFile.rb:
class DesignFile < ActiveRecord::Base
belongs_to :step
...
end
I'm running into issues when I try to add an additional file after another file has already been added. This is what the form looks like in html:
<div class="upload_new_files">
<fieldset class="design_file_upload_fieldset">
<label for="step_design_files_attributes_0_design_file_path">test.pdf</label>
<input id="step_design_files_attributes_0__destroy" name="step[design_files_attributes][0][_destroy]" type="hidden" value="false">
<i class="icon-trash"></i> Remove File
<input id="step_design_files_attributes_0_step_id" name="step[design_files_attributes][0][step_id]" type="hidden" value="451">
<input id="step_design_files_attributes_0_project_id" name="step[design_files_attributes][0][project_id]" type="hidden" value="120">
<input id="step_design_files_attributes_0_user_id" name="step[design_files_attributes][0][user_id]" type="hidden" value="15">
<hr>
</fieldset>
<fieldset class="design_file_upload_fieldset">
<input id="step_design_files_attributes_70265135732560_design_file_path" name="step[design_files_attributes][70265135732560][design_file_path]" onchange="javascript: addField(this);" type="file" style="display: none;">
<input id="step_design_files_attributes_70265135732560__destroy" name="step[design_files_attributes][70265135732560][_destroy]" type="hidden" value="false">
<i class="icon-trash"></i> Remove File
<input id="step_design_files_attributes_70265135732560_step_id" name="step[design_files_attributes][70265135732560][step_id]" type="hidden" value="446">
<input id="step_design_files_attributes_70265135732560_project_id" name="step[design_files_attributes][70265135732560][project_id]" type="hidden" value="120">
<input id="step_design_files_attributes_70265135732560_user_id" name="step[design_files_attributes][70265135732560][user_id]" type="hidden" value="15">
<hr>
</fieldset>
</div>
But when I submit the form, the design_files_attributes aren't in the parameters. I'm expecting something like this:
Parameters: {"step"=>{ "design_files_attributes"=>{"0"=>{"id"=>"17" ...}}, "70265135732560"=>{...}...}
But, instead, I'm getting this:
Parameters: {"step"=> {"design_files_attributes"=>{"0"=>{"id"=>"17" ...}}
What isn't my new entry being submitted in the form parameters?
Edit
Here is the full params that are being submitted to the controller; I didn't include it above because it includes a lot of other things, but I have it here in case it's useful:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...=", "step"=>{"name"=>"New Design File", "description"=>"", "question_attributes"=>{"description"=>"", "decision_attributes"=>{"description"=>"", "question_id"=>""}}, "design_files_attributes"=>{"0"=>{"_destroy"=>"false", "step_id"=>"451", "project_id"=>"120", "user_id"=>"15", "id"=>"17"}}, "last"=>"0", "published_on_date"=>"02/04/2014", "published_on_time"=>"05:02 PM", "timeZone"=>"EST"}, "commit"=>"Update Step", "project_id"=>"120", "id"=>"3"}
_step_form.html.erb
<%= semantic_form_for [#project, #step], :html => {:multipart => true} do |f| %>
<%= f.fields_for :design_files, multipart: true do |design_file_form| %>
<%= render 'design_file_fields', f: design_file_form %>
<% end %>
<p class="add_design_file" style="display:none;"><%= link_to_add_fields raw("<icon class='icon-plus-sign' style='color:green;'></icon> Add Another Design File"), f, :design_files %></p>
<% end %>
_design_file_fields.html.erb
<fieldset class="design_file_upload_fieldset">
<% if f.object.new_record? %>
<%= f.file_field :design_file_path, :onChange=>"javascript: addField(this);" %>
<% else %>
<%= f.label :design_file_path, f.object.design_file_path.to_s.split('/').pop() %>
<% end %>
<%= f.hidden_field :_destroy %>
<%= link_to raw('<i class="icon-trash"></i> Remove File'), "#", class: "remove_fields" %>
<%= f.hidden_field :step_id, :value => #step.id %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<hr>
</fieldset>
steps.js
// add a new design file field to the form
function addField(input) {
console.log('add design field input');
// remove the filefield of recently uploaded file and replace with existing file styling
var filename = $(input).val().split('\\').pop();
$(input).parent('fieldset').prepend('<label>'+filename+'</label>');
$(input).hide();
$('.add_fields').click();
}
application_helper.rb
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
Wow, the answer ended up being totally unrelated to what I had posted! The issue was with the form itself, which had an extra <% end %> element, causing the design files fields_for not being incorporated into the form. I'll leave this response in case it's useful for anyone else.

How do I convert this dropdown to a f.select in Rails?

I'm a Rails beginner and I'm working on a preexisting Rails 2 project. In my application, I tried converting a select dropdown field to a form_handler f.select, but I'm getting this error:
undefined method `location.masterlocation.name
Here is my attempt:
<% form_for #newsavedmap, :html=>{:id=>'createaMap'} do |f| %>
<%= f.select(:start, options_from_collection_for_select(#itinerary.locations, "location.masterlocation.street_address, location.masterlocation.city, location.masterlocation.state, location.masterlocation.zip", "location.masterlocation.name"), :id => "startdrop")%>
Here is the original dropdown field:
<select id="startdrop">
<option value="">
<% for location in #itinerary.locations %>
<option value="<%= location.masterlocation.street_address %> <%= location.masterlocation.city %>, <%= location.masterlocation.state %>, <%= location.masterlocation.zip %>"><%= location.masterlocation.name %></option>
<% end %>
</select>
Thanks in advance for your help!
edit 1
I've gotten much closer using this code:
<%= f.select :start, options_for_select(#itinerary.locations.map{ |c| [c.masterlocation.name, c.masterlocation.street_address]}),{}, :id=>"startdrop", :name=>"startthere" %>
The problem is that I want to include the city, state, and zip in the value, all separated by commas. Any ideas about how to do this?
<%= f.select :start, options_for_select(#itinerary.locations.map{ |c| [c.masterlocation.inst_name, c.masterlocation.street_address AND , AND c.masterlocation.city AND , AND c.masterlocation.state AND, AND c.masterlocation.zip]}),{}, :id=>"startdrop", :name=>"startthere" %>
THIS WORKS!
Maptry Helper:
module MaptryHelper
def options_for_select(locations)
locations.map do |location|
[location.masterlocation.name, location_string(location.masterlocation)]
end
end
def location_string(masterlocation)
"#{masterlocation.street_address}, #{masterlocation.city}, #{masterlocation.state}, #{masterlocation.zip}"
end
end
View
<%= f.select :start, options_for_select(#itinerary.locations),{}, :id=>"startdrop", :name=>"startthere" %>
Place the following in a helper file
def select_options_for_locations(locations)
locations.map do |location|
[location_string(location.masterlocation), location.masterlocation.street_address]
end
end
def location_string(masterlocation)
"#{masterlocation.city}, #{masterlocation.state}, #{masterlocation.zip} #{masterlocation.name}"
end
Then in your view, you can use the following
= f.select :start, select_options_for_locations(#itinerary.locations), {}, :id => "startdrop"

How do I change the html tag and class generated by fields_for?

This is a simple question that I'm kinda ashamed to ask, but I've been banging my head against the wall and navigating through the rails 3 documentation without any success :/
So, here is the thing:
When I use the fields_for helper it wraps the generated fields in a <div class="fields"> ... </div> tag.
so, my code is
<ul class="block-grid two-up">
<%= f.fields_for :images do |image_builder| %>
<%= render "images/form", :f => image_builder %>
<% end %>
</ul>
and the generated html is:
<ul class="block-grid two-up">
<div class="fields">
<div>
<label for="company_images_attributes_0_image"> Image</label>
<input id="company_images_attributes_0_image"
name="company[images_attributes][0][image]" type="file">
</div>
</div>
<div class="fields">
<div>
<label for="company_images_attributes_1_image"> Image</label>
<input id="company_images_attributes_1_image"
name="company[images_attributes][1][image]" type="file">
</div>
</div>
</ul>
What I want to do is actually change the <div class="fields"> wrapper tag to <li>.
The documentation says you can pass options to the fields_for, but its not clear about what options you can pass, maybe you can change this wrapper tag?
A possibility could be to override a function, kinda like ActionView::Base.field_error_proc when there is an error in the form.
Quick edit: I forgot to mention that I'm using simple_form to generate this form. I tried looking in the simple_form.rb config file for a way to customize this, but I didn't see any way of doing it.
Solution
After further investigation, it turns out the form was using the nested_form gem as well to generate the form (not only simple_form). This generator was causing the fields_for to be wrapped in the div tag. Thanks everybody for their suggestions!
The following disables the wrapper:
f.fields_for :images, wrapper:false do |image_builder|
then you can add your own wrapper in the builder block.
A cheap solution would be just adding <li> tag into the form like:
<%= f.fields_for :images do |image_builder| %>
<li><%= render "images/form", :f => image_builder %></li>
<% end %>
I am not sure if you can completely eliminate the div tag by passing some params to field_for. But I think you can change the name of div class or id by passing the html block, like in form_for:
<%= form_for #image, :as => :post, :url => post_image_path,
:html => { :class => "new_image", :id => "new_image" } do |f| %>
You said you're using simple_form then you should be saying <%= f.simple_fields_for... Have you tried using wrapper_html option:
<%= f.input :name, :label_html => { :class => 'upcase strong' },
:input_html => { :class => 'medium' }, :wrapper_html => { :class => 'grid_6 alpha' } %>
Edit 1:
From SimpleForm documentation:
Wrapper
SimpleForm allows you to add a wrapper which contains the label, error, hint and input. The first step is to configure a wrapper tag:
SimpleForm.wrapper_tag = :p
And now, you don't need to wrap your f.input calls anymore:
<%= simple_form_for #user do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= f.button :submit %>
<% end %>
Edit 2:
And there is this config option with which you can say what css class to use with the wrapper elements:
config/initializers/simple_form.rb
# CSS class to add to all wrapper tags.
config.wrapper_class = :input

Resources