I'm new to Rails and I'm running in circles trying to implement a Select2 input with multiple selections. I've read the solutions on StackOverflow and none of them helped me, which typically means I'm doing something unusually wrong.
When I'm submitting my form it sends my federation_list parameter multiple times with different values (in headers):
event[federation_list]: WDC
event[federation_list]: WDSF
I need all of the values in one Federation parameter seperated by a comma, that I can then split in my controller.
I've tried adding name: "federation_list[]" as it says in the Select2 documentation, but my form_with overwrites it so it loses the brackets.
Updated! I figured out the solution and this is my working code:
select2_initializer.js
$( document ).on('turbolinks:load', function() {
$( "#event_federation_list" ).select2({
theme: "bootstrap",
multiple: true,
placeholder: 'Select Federation(s)',
width: 'style'
}).val('').trigger('change');
});
_form.html.erb
<div class="form-group">
<%= form.label :federation_list, value: "Federations", class: "form-label" %>
<%= form.select :federation_list, options_from_collection_for_select(Federation.all, :id, :name, {:selected => #event.federations.map(&:id)}), {}, { multiple: true } %>
</div>
The :selected makes the selected options appear when the user edits the form.
event.rb
def federation_list
self.federations.map(&:name).join(', ')
end
def federation_list=(names)
self.federations = names.split(',').map do |n|
Federation.where(name: n.strip).first_or_create!
end
end
If it is relevant information my Event has_many: Federations, through: Event_Federations.
GitHub rep
I ended up going with Selectize instead of Select2, but the solution still holds true for Select2.
And then I wrote this piece of code in my event.rb:
def federation_list
self.federations.map(&:name).join(', ')
end
def federation_list=(ids)
self.federations = ids.reject!(&:empty?).map do |id|
Federation.where(id: id).first
# Use .first_or_create! to let the user add new objects
end
end
I used SteveTurczyns advise and added federation_list: [] in my strong params.
And that works!
not sure if this is your only problem but a definite problem is that strong parameters require that you explicitly specify if a model parameter is an array.
Instead of
params.require(:event).permit(:title, :country,
:city, :start_date, :end_date, :published_at, :user_id,
:federation_list)
You should have
params.require(:event).permit(:title, :country,
:city, :start_date, :end_date, :published_at, :user_id,
federation_list: [])
Related
I have implemented the functionality of adding tags to a post in Rails using chosen-rails and acts-as-taggable gems. I want to further enhance the functionality by making user to create new tag if it doesn't exist.
form.html.slim
.form-group.text-center
= f.label :tag_ids, "Tags"
= f.collection_select :tag_ids, Tag.order(:name), :id, :name, {}, {multiple: true}
I need to add a new tag if it doesn't exist.
Ok with Selectize.js, Acts_as_Taggable, & SimpleForm:
Firstly, change your input from collection_select to a normal string input. Selectize.js will separate all of the values into tags anywhere there is a comma. This happens behind the scenes so as people add tags, it's actually inserting a long string into your database with whatever delimiter you supply. Then you need to add an id to that field so you can initialize selectize such as:
<%= f.input :nationalities, as: :string, placeholder: "Nationalities", input_html: { id: 'nationality-input'} %>
Then initialize selectize.js:
#The following line gets all tags ever posted for a user with the context 'nationalities' which we will use as options.
<%=nations = ActsAsTaggleOn::Tagging.includes(:tag).where(context: 'nationalities').uniq.pluck(:id, :name)
$(document).ready(function() {
$('#nationality-input).selectize({
delimiter: ',',
persist: true,
allowEmptyOption: false,
options: [
<% nations.each do |nat| %>
{text: '<%=nat[1] %>', value: '<%=nat[1]%>' },
<% end %>
searchField: 'text',
create: function(input) {
return {
value: input,
text: input
}
}
});
});
Make sure you have acts_as_taggable setup properly and that the corresponding model includes it. Then in your controller just save the whole string with commas and all and allow selectize to reformat it on views automagically.
I am trying to use collection_select tag for the default _form.html.erb using a concern/module, I need to set a hash including some department names.
Here is my app/models/concerns/SetDepartment.rb
module Set_Department
extend ActiveSupport :: Concern
def department
department {
1=>"Amatitlán",
2=>"Chinautla",
3=>"Chuarrancho"
}
end
end
Here is the model where I want to call the department method:
class Aplicante < ActiveRecord::Base
include SetDepartment
validates :titulo_id, :primer_nombre,
:primer_apellido, :dpi, :direccion_linea_1,:zona, :department_id, :username,
presence: true
validates :dpi,:username, uniqueness: true
has_secure_password
end
Now, I need to include this hash in a collection_select tag on my app/views/applicants/_form.html.erb
#...
<div class="field">
<%= f.label :department_id %><br>
<%= f.collection_select :department_id, Aplicante.department, Aplicante.department %>
</div>
#...
Obviously, this does not work but I can not think on anything else.
I have searched through the internet but I just get tough explinations and none of them involves a module... is it even possible?
Solved!
I was using the wrong method..
We can not use a collection_select helper with a hash, instead, we need to use the regular select method.
Collection_select is used when you have two models and you want to combine their different values in a drop down menu.
Information about how to use the select tag with a hash here:
http://apidock.com/rails/ActionView/Helpers/FormTagHelper/select_tag
I have a form that I want to validate if an option has been selected.
I do not want to accept the default starting option, "Please Select a Product"
I using this now and get a uninitialized contant error. I believe I am writing the syntax wrong.
Controller:
validates :product_name, :presence => { :unless => (product_name = "Please Select a Product")}
View:
<span class="span5 pagination-right">
<%= f.label "Product" %>
<%= f.select :product_name, options_for_select([ ["Please Select a Product"] ]) %>
</span>
How am I supposed to have the option written?
Thank you
The product_name is changed like this:
<script>
$(document).ready(function() {
$('#ticket_product_name').html("<option>Please Select a Product</option>");
$('#ticket_firmware_version_string').html("<option>Please Select a Firmware</option>");
$('#category').change(function(){
$('#ticket_product_name').html("<option>Please Select a Product</option>");
$('#ticket_firmware_version_string').html("<option>Please Select a Firmware</option>");
if ($('#category').val() == "blah")
{
$('#ticket_product_name').append("<option>blah</option>");
$('#ticket_product_name').append("<option>blah</option>");
}
else if ($('#category').val() == "another category")
{
$('#ticket_product_name').append("<option>blah product</option>");
}
until end of options, end script.
I think what you are looking for is a placeholder for this select. In this case, I used a disabled option:
<% options = options_for_select(["Select an option", ["Product #1",1], ["Product #2",2]], disabled: "Select an option") %>
<%= f.select :product_name, options %>
You should use include_blank from the select helper, like this:
<%= f.select :product_name, options_for_select([["opt1",1],["opt2",2]]), include_blank: true %>
and then, on model
validates :product_name, :presence => true
#MrYoshiji commented something truthful in my answer - if you are not allowing it to be blank, the easy way is simply not add a blank option in the select
I would recommend to use the :prompt option for your select helper (the documentation on this isn't quite straight forwward, but it's ok in this version of the same thing: http://apidock.com/rails/ActionView/Helpers/FormTagHelper/select_tag). This allows you to define a helper string of text to encourage the user to select an option, but will submit a nil value back when the form is submitted.
<%= f.select :product_name, PRODUCT_NAME_OPTIONS, prompt: 'Select an option' %>
The next thing I'd recommend is... instead of validating presence of on the model, do a validates_inclusion_of. This way you can define the set of options in the model and then use that same set of options in the select helper, above, as well is in your validation. This way you know the form wasn't manipulated to include a different option and it actually keeps things a bit DRYer.
Product
PRODUCT_NAME_OPTIONS = {'opt 1' => 'opt1', 'opt 2' => 'opt2'}
validates_inclusion_of :product_name, :in => PRODUCT_NAME_OPTIONS.values, :message => 'choose from the available options'
Instead of making this too complicated, I just added a length validation since the default is much longer then the product names. Simple is best, however there should be a string validator option.
validates :product_name, presence: true, length: {
maximum: 21,
too_long: ": Must select a product"
}
I use simple_form to display an email field:
= f.simple_fields_for :user do |f|
= f.input :email, wrapper: :append, pattern: false do
= f.input_field :email, type: "email"
Somehow it always set's the pattern for the input field but I want to use the HTML5 validation of the email input field instead.
Is there a way to prevent simpleform from setting the pattern?
You can monkey-patch SimpleForm:
module SimpleForm
module Components
module Pattern
def pattern
# Deactivated for good:
#input_html_options[:pattern] ||= pattern_source
nil
end
end
end
end
Now, pattern will no longer be generated from validations. You need to add pattern manually to each input that requires a pattern. Example:
= f.input :name, pattern: "[a-zA-Z]+"
Had the same problem ... found a workaround but its kind of hacky
add
:pattern => ".*"
to your field like this
<%= f.input_field :email, :autofocus => true, :pattern => ".*", :class => "span12", :placeholder => t('placeholder.email') %>
I would rather make a string_input.rb file in the app directory some where ... may be in a folder named 'inputs' then have this code
class StringInput < SimpleForm::Inputs::StringInput
def input
input_html_options[:class] = "input-xlarge #{input_html_options[:class]}"
unless string?
input_html_classes.unshift("string")
input_html_options[:type] ||= input_type if html5?
end
input_html_options[:pattern] = nil
add_size!
#builder.text_field(attribute_name, input_html_options)
end
end
This will have effect on all the pattern attributes so no need to put it in explicitly. of course you can also add a condition if the input type is email or not if you want to specify.
Happy coding :)
I'm trying to fill out an array with values from checkboxes. It works just fine when creating a record, but fails validation when editing. The params look right, which is what really confuses me:
"record"=>{... "type_array"=>["accounting"], ...}
It looks the same as the params from creating a new record. The fields in New.html.erb and Edit.html.erb also use the same markup.
Edit.html.erb
<div class="field">
<%= f.label :type_array, "What type of record?" %><br />
<% ["accounting", "agriculture", "automotive"].each do |type| %>
<%= check_box_tag 'record[type_array][]', type, (true if #record.type_list.include? type),
:id => type %>
<%= label_tag type, type.titleize, :class => type %><br />
<% end %>
</div>
Parts of Record.rb
validates :type_array, :presence => true
attr_accessor :type_array
attr_accessible :type_array
before_validation :set_type_list
private
def set_type_list
self.type_list = type_array.join ',' if type_array.present?
end
Am I missing something? When I remove the type_array validation and fill out the form, it acts like type_array is empty. Somewhere along the line, it must be lost or something.
I appreciate any help.
(Sidenote: if anyone has a better way to do the list of checkboxes, let me know)
Delete the line attr_accessor :type_array.
This creates accessor methods to a new instance variable, not to the model attribute type_array, which means that #record.type_array now refers to that instance variable instead of the attribute.
You almost never use attr_accessor or it's siblings attr_reader and attr_writer in Rails because you want to deal with model attributes, not instance variables.
Edit: You're using type_array as a virtual attribute.
class Record < ActiveRecord::Base
validates :type_array, :presence => true
attr_accessible :type_array
def type_array=(val)
self.type_list = val.join ','
end
def type_array
self.type_list.split ','
end
def type_array_before_type_cast
type_array
end
end
For the reason why you need that last function definition, see this question.