Why Is The First Element Always Blank in My Rails Multi-Select, Using an Embedded Array

Why is the first element always blank in my Rails multi-select, using an embedded array?

The hidden field is what is causing the issue. But it is there for a good reason: when all values are deselected, you still receive a subset_array parameter. From the Rails docs (you may have to scroll to the right to see all of this):

  # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
# any mass-assignment idiom like
#
# @user.update_attributes(params[:user])
#
# wouldn't update roles.
#
# To prevent this the helper generates an auxiliary hidden field before
# every multiple select. The hidden field has the same name as multiple select and blank value.
#
# This way, the client either sends only the hidden field (representing
# the deselected multiple select box), or both fields. Since the HTML specification
# says key/value pairs have to be sent in the same order they appear in the
# form, and parameters extraction gets the last occurrence of any repeated
# key in the query string, that works for ordinary forms.

EDIT: The last paragraph suggests that you shouldn't be seeing the empty one in the case when something is selected, but I think it is wrong. The person who made this commit to Rails (see https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885) is trying to do the same trick that Rails uses for checkboxes (as mentioned here: https://github.com/rails/rails/pull/1552), but I don't think it can work for a multiple select box because the parameters sent over form an array in this case and so no value is ignored.

So my feeling is that this is a bug.

first item in array from the multiple collection_select is always blank

The empty artist_id is important. On another form, you may have omitted the artist select altogether, in which case, the artists association should not be affected.

If the artist select is included, and you de-select all artists, the artists need to be removed from the artists association. Normal HTML behavior would not include the artist_id parameter in the PUT at all when nothing is selected. Your controller in that case would think that you do not want to modify the artists association at all.

To solve this, the collection_select includes a hidden field with a blank value to let the controller know that the form intends to alter the artists association. If no artists have been selected, that blank element in the array will ensure all artists are removed from the association.

rails select list with multiple set to true will always add a blank entry to the array returned, how to remove it

To remove the empty string from the array, use #reject.

params["lalaemail"]["to"].reject(&:blank?)

But let's address your second question before we clean that up:

First ensure that your Email table has to column with array set to true—if it doesn't, create a migration for it (and maybe give it a better name, like recipients):

class AddRecipientsColumnToEmail < ActiveRecord::Migration[5.1]
def change
add_column :emails, :recipients, :text, array: true, default: []
end
end

After you run that migration, each instance of an email will have the ability to save as many recipients as you want.

Now, let's circle back to using #reject.

Instead of manipulating the dirty recipients array you get back from the view in your controller, let's offload removing the empty strings to the Email model:

Email.rb

class Email < ApplicationRecord
# Your associations
# Your validations

# Define a before_validation hook that will sanitize your input
# Example:
before_validation :sanitize_recipients

# More public model methods you may have

private

# Define your sanitation method; consider if there's anything else
# you'd want to do to clean the data--perhaps remove duplicates?
#
# Returns an Array.
def sanitize_recipients
self.recipients = recipients.reject(&:blank?)&.uniq
end
end

If you're unfamiliar with before_validation--it's built-in to Rails and basically tells your model: "Hey wait, before you check this data, let me do this first.", where "this" can be any method you define. In this case, we defined a method to sanitize the recipients Array of any duplicate or blank values.

And voila, the empty string you get back is no longer an issue, and you have the ability to save all of an email's recipients.

rails form select multiple gives empty first value

You can reject blank option by passing :include_blank => false

<%= f.select :recipes, Recipe.all.collect { |x| [x.name, x.id]}, {:include_blank => false}, :multiple => true %>

And you can set an prompt as following

<%= f.select :recipes, Recipe.all.collect { |x| [x.name, x.id]}, {:include_blank => "Please Select"}, :multiple => true %>

Multiple Select Sending Empty String to Rails Controller

It seems this issue was also reported in another question: Why is the first element always blank in my Rails multi-select, using an embedded array?

They outline the specification for it and how it will be resolved in Rails 4. The second most popular response says how to apply a fix for it in the mean time.

Rails multiple select puts extra blank parameter

About your question has been answered in collection select always adding blank value.

You will get [""] when you did not select anything or you select your options select as default. To avoid this you have to add hidden_field before the collection select.

<div class="field">
<%= f.label :subscription_lists %><br>
<%= f.hidden_field :subscription_lists %>
<%= f.collection_select(:subscription_lists, SubscriptionList.all, :id, :name, {}, {:multiple => true}) %>
</div>

The hidden_field helps you when it's nothing selected. How about when you choose it? Please try this.

  def update
if student_params["subscription_lists"].any?
student_params["subscription_lists"].reject!(&:empty?)
end
respond_to do |format|
if @student.update(student_params)
format.html { redirect_to @student, notice: 'Student was successfully updated.' }
format.json { render :show, status: :ok, location: @student }
else
format.html { render :edit }
format.json { render json: @student.errors, status: :unprocessable_entity }
end
end
end

I hope this help you.



Related Topics



Leave a reply



Submit