robmclarty robmclarty - 1 month ago 10
HTML Question

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

I'm using Rails 3.2.0.rc2. I've got a

Model
, in which I have a static
Array
which I'm offering up through a form such that users may select a subset of
Array
and save their selection to the database, stored in a single column in
Model
. I've used serialize on the database column which stores the
Array
and Rails is correctly converting the users' selections into Yaml (and back to an array when reading that column). I'm using a multi-select form input to make selections.

My problem is that, the way I currently have it, everything works as I would expect except that the user's subset array always has a blank first element when it's sent to the server.

This isn't a big deal, and I could write code to cut that out after the fact, but I feel like I'm just making some kind of syntactical error as it doesn't seem to me that the default Rails behaviour would intentionally add this blank element without some reason. I must have missed something or forgot to disable some kind of setting. Please help me understand what I'm missing (or point me in to some good documentation that describes this with more depth than what I've been able to find on the intertubes).

MySQL Database Table 'models':




  • includes a column named
    subset_array
    which is a TEXT field



Class Model includes the following settings:




  • serialize :subset_array

  • ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]



Form for editing Models includes the following input option:




  • f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => @model.subset_array



PUT to server from client looks something like this:




  • assuming only value1 and value3 are selected

  • "model" => { "subset_array" => ["", value1, value3] }



Database update looks like this:




  • UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'



As you can see, there's this extra, blank, element in the array being sent and set in the database. How do I get rid of that? Is there a parameter I'm missing from my
f.select
call?

Much thanks appreciated :)

EDIT: This is the generated HTML code from the
f.select
statement. It looks as though there is a hidden input being generated which may be the cause of my issue? Why is that there?

<input name="model[subset_array][]" type="hidden" value>
<select id="model_subset_array" multiple="multiple" name="model[subset_array][]" selected="selected">
<option value="value1" selected="selected">Value1</option>
<option value="value2">Value2</option>
<option value="value3" selected="selected">Value3</option>
<option...>...</option>
</select>

Answer

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.