¿Por qué el primer elemento siempre está en blanco en mi selección múltiple de Rails, usando una matriz incrustada?


Estoy usando Rails 3.2.0.rc2 . Tengo un Model, en el que tengo un Array estático que estoy ofreciendo a través de un formulario tal que los usuarios pueden seleccionar un subconjunto de Array y guardar su selección en la base de datos, almacenada en una sola columna en Model. He utilizado serialize en la columna de la base de datos que almacena el Array y Rails está convirtiendo correctamente las selecciones de los usuarios en Yaml (y de vuelta a una matriz al leer esa columna). Estoy usando una entrada de formulario de selección múltiple para hacer selección.

Mi problema es que, de la forma en que lo tengo actualmente, todo funciona como esperaría, excepto que la matriz de subconjuntos del usuario siempre tiene un primer elemento en blanco cuando se envía al servidor.

Esto no es un gran problema, y podría escribir código para cortar eso después del hecho, pero siento que solo estoy haciendo algún tipo de error sintáctico, ya que no me parece que el comportamiento predeterminado de Rails agregaría intencionalmente este elemento en blanco sin alguna razón. Me debe haber perdido algo o se olvidó de desactivar algún tipo de configuración. Por favor, ayúdame a entender lo que me falta (o indícame alguna buena documentación que describa esto con más profundidad de lo que he podido encontrar en los intertubes).

Tabla de base de datos MySQL 'modelos':

  • incluye una columna llamada subset_array que es un campo de TEXTO

El modelo de clase incluye los siguientes ajustes:

  • serialize :subset_array
  • ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]

Formulario para la edición de modelos incluye la siguiente opción de entrada:

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

PONER en el servidor desde el cliente se ve algo como esto:

  • suponiendo que solo se seleccionen value1 y value3
  • "model" => { "subset_array" => ["", value1, value3] }

La actualización de la base de datos se ve así:

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

Como puede ver, hay este elemento extra, en blanco, en la matriz que se envía y se establece en la base de datos. ¿Cómo me deshago de eso? Hay un parámetro que me falta de mi f.select llamar?

Muchas gracias apreciadas:)

EDIT : Este es el código HTML generado a partir de la instrucción f.select. Parece que hay una entrada oculta que se genera que puede ser la causa de mi problema? ¿Por qué está ahí?

<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>
Author: robmclarty, 2012-01-19

9 answers

El campo oculto es lo que está causando el problema. Pero está ahí por una buena razón: cuando todos los valores están deseleccionados, todavía recibe un parámetro subset_array. Desde los documentos de Rails (puede que tengas que desplazarte hacia la derecha para ver todo esto):

  # 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: El último párrafo sugiere que no deberías ver el vacío en el caso de que se seleccione algo, pero creo que está mal. La persona que hizo este commit a Rails (ver https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885 ) está tratando de hacer el mismo truco que Rails utiliza para las casillas de verificación (como se menciona aquí: https://github.com/rails/rails/pull/1552 ), pero no creo que pueda funcionar para un cuadro de selección múltiple porque los parámetros enviados forman una matriz en este caso y por lo tanto no se ignora ningún valor.

Así que mi sensación es que esto es un error.

 51
Author: Mike A.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-19 21:15:46

En los carriles 4:

Podrás pasar la opción :include_hidden. https://github.com/rails/rails/pull/5414/files

Como solución rápida por ahora: puede usar ahora mismo en su modelo:

before_validation do |model|
  model.subset_array.reject!(&:blank?) if model.subset_array
end

Esto simplemente eliminará todos los valores en blanco a nivel del modelo.

 64
Author: Bogdan Gusiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-06 13:41:57

Otra solución rápida es usar este filtro de controlador:

def clean_select_multiple_params hash = params
  hash.each do |k, v|
    case v
    when Array then v.reject!(&:blank?)
    when Hash then clean_select_multiple_params(v)
    end
  end
end

De esta manera se puede reutilizar entre controladores sin tocar la capa del modelo.

 10
Author: Max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-31 19:21:03

Http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box

Gotcha

La especificación HTML dice que las casillas de verificación o selecciones sin marcar no tienen éxito, y por lo tanto los navegadores web no los envían. Desafortunadamente esto introduce un gotcha: si un modelo de factura tiene una bandera de pago, y en la forma que edita una factura pagada el usuario desmarca su casilla de verificación, no paga se envía el parámetro. Entonces, cualquier modismo de asignación masiva como

@factura.update (params[:invoice]) no actualizaría el indicador.

Para evitar esto, el helper genera un campo oculto auxiliar antes la misma casilla de verificación. El campo oculto tiene el mismo nombre y su los atributos imitan una casilla de verificación sin marcar.

De esta manera, el cliente envía solo el campo oculto (que representa la casilla de verificación está desactivada), o ambos campos. Desde el HTML la especificación dice que los pares clave / valor deben enviarse el mismo orden aparecen en el formulario, y la extracción de parámetros obtiene el último ocurrencia de cualquier clave repetida en la cadena de consulta, que funciona para formas ordinarias.

Para eliminar los valores en blanco:

  def myfield=(value)
    value.reject!(&:blank?)
    write_attribute(:myfield, value)
  end
 5
Author: devishot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-03 05:25:49

En Rails 4+ establece: include_hidden en select_tag a false

<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>
 4
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-01 15:07:36

En el controlador:

arr = arr.delete_if { |x| x.empty? }
 3
Author: Mauro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 08:00:38

Lo arreglé usando el params[:review][:staff_ids].delete("") en el controlador antes de la actualización.

En mi opinión:

= form_for @review do |f|
  = f.collection_select :staff_ids, @business.staff, :id, :full_name, {}, {multiple:true}
= f.submit 'Submit Review'

En mi controlador:

class ReviewsController < ApplicationController
  def create
  ....
    params[:review][:staff_ids].delete("")
    @review.update_attribute(:staff_ids, params[:review][:staff_ids].join(","))
  ....
  end
end
 0
Author: Bruno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-14 17:30:15

Hago que funcione escribiendo esto en la parte Javascript de la página:

$("#model_subset_array").val( <%= @model.subset_array %> );

El mío se parece más a lo siguiente:

$("#modela_modelb_ids").val( <%= @modela.modelb_ids %> );

No estoy seguro de si esto me va a dar dolor de cabeza en el futuro, pero ahora funciona bien.

 0
Author: imaginabit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 08:00:15

Usar jQuery:

$('select option:empty').remove(); 

Opción para eliminar las opciones en blanco del menú desplegable.

 -2
Author: user1875926,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 07:59:28