2009-06-16 19 views
50

que tienen un modelo de datos en mi proyecto Rails que tiene un campo en serie:¿Cómo editar un campo serializado de Rails en un formulario?

class Widget < ActiveRecord::Base 
    serialize :options 
end 

El campo de opciones puede tener información de datos variables. Por ejemplo, aquí está el campo de opciones para un registro del archivo de accesorios:

options: 
    query_id: 2 
    axis_y: 'percent' 
    axis_x: 'text' 
    units: '%' 
    css_class: 'occupancy' 
    dom_hook: '#average-occupancy-by-day' 
    table_scale: 1 

Mi pregunta es ¿cuál es la forma correcta de permitir a un usuario editar esta información en una vista de formulario estándar?

Si solo utiliza un campo de área de texto simple para el campo de opciones, obtendrá una representación de volcado yam y los datos se enviarán nuevamente como una cadena.

¿Cuál es la mejor forma de editar un campo hash serializado como este en Rails?

+0

¿Qué tipo de interfaz te gustaría? ¿Debería haber campos para cada atributo? ¿Sabes cuáles son todos los atributos por adelantado? –

+0

Todos los atributos no se conocerán por adelantado. Algunos son estándar y siempre estarán presentes, pero el resto puede ser definido por el usuario. Esta es una interfaz solo de administrador, por lo que confío en la entrada del usuario en un grado mucho mayor de lo normal. De hecho, acabo de utilizar un cuadro de texto y dejé que el usuario ingrese la clave: pares de valores usando el marcado YAML y funcionó bien en – cpjolicoeur

Respuesta

55

Si sabe lo que las teclas de opción van a ser de antemano, puede declarar captadores y definidores especiales para ellos, así:

class Widget < ActiveRecord::Base 
    serialize :options 

    def self.serialized_attr_accessor(*args) 
    args.each do |method_name| 
     eval " 
     def #{method_name} 
      (self.options || {})[:#{method_name}] 
     end 
     def #{method_name}=(value) 
      self.options ||= {} 
      self.options[:#{method_name}] = value 
     end 
     attr_accessible :#{method_name} 
     " 
    end 
    end 

    serialized_attr_accessor :query_id, :axis_y, :axis_x, :units 
end 

Lo bueno de esto es que expone los componentes de la opciones matriz como atributos, lo que le permite utilizar los carriles forman ayudantes de este modo:

#haml 
- form_for @widget do |f| 
    = f.text_field :axis_y 
    = f.text_field :axis_x 
    = f.text_field :unit 
+0

Gracias, pero no conozco todos los valores de las opciones con anticipación. Algunos de ellos siempre estarán presentes, pero el resto puede ser definido por el usuario. – cpjolicoeur

+0

Sí, sería genial crear esos accesos dinámicamente – Artur79

+1

es bueno agregar al final de la evaluación: attr_accessible method_name – Artur79

1

estoy tratando de hacer algo similar y me encontré con este tipo de obras:

<%= form_for @search do |f| %> 
    <%= f.fields_for :params, @search.params do |p| %> 
     <%= p.select "property_id", [[ "All", 0 ]] + PropertyType.all.collect { |pt| [ pt.value, pt.id ] } %> 

     <%= p.text_field :min_square_footage, :size => 10, :placeholder => "Min" %> 
     <%= p.text_field :max_square_footage, :size => 10, :placeholder => "Max" %> 
    <% end %> 
<% end %> 

excepto que los campos del formulario no se rellenan cuando se procesa el formulario. cuando se envía el formulario los valores vienen a través de bien y que puedo hacer:

@search = Search.new(params[:search]) 

por lo que su "medio" de trabajo ...

28

EMH es casi allí. Pensaría que Rails devolvería los valores a los campos de formulario, pero no es así. Entonces puede ponerlo ahí manualmente en el parámetro ": value =>" para cada campo. No se ve bien, pero funciona.

Aquí es de arriba a abajo:

class Widget < ActiveRecord::Base 
    serialize :options, Hash 
end 

<%= form_for :widget, @widget, :url => {:action => "update"}, :html => {:method => :put} do |f| %> 
<%= f.error_messages %> 
    <%= f.fields_for :options do |o| %> 
     <%= o.text_field :axis_x, :size => 10, :value => @widget.options["axis_x"] %> 
     <%= o.text_field :axis_y, :size => 10, :value => @widget.options["axis_y"] %> 
    <% end %> 
<% end %> 

Cualquier campo se agrega en el "fields_for" se mostrará en el hash serializado. Puede agregar o eliminar campos a voluntad. Se pasarán como atributos al hash "options" y se almacenarán como YAML.

+0

Estoy tratando de hacer lo que me dijiste, pero parece que no funciona para mí: http://stackoverflow.com/questions/6621341 –

1

Voy a sugerir algo simple, porque todo el tiempo, cuando el usuario guardará la forma Obtendrá la cadena. Así que usted puede utilizar, por ejemplo, antes del filtro y analizar esos datos así:

before_save do 
    widget.options = YAML.parse(widget.options).to_ruby 
end 

, por supuesto, se debe añadir la validación si esto es correcto YAML. Pero debería funcionar.

3

No hay necesidad setter/getters, acabo definidas en el modelo:

serialize :content_hash, Hash 

A continuación, en la vista, lo hago (con simple_form, pero similar con rieles de vainilla):

= f.simple_fields_for :content_hash do |chf| 
    - @model_instance.content_hash.each_pair do |k,v| 
     =chf.input k.to_sym, :as => :string, :input_html => {:value => v} 

Mi El último problema es cómo permitir que el usuario agregue un nuevo par clave/valor.

+0

Mayormente trabajado, excepto que estoy tratando con un hash profundo y solo termina el nivel raíz como un hash con esto. – cpuguy83

35

Bueno, tuve el mismo problema y traté de no sobre-diseñarlo. El problema es que, aunque puede pasar el hash serializado a fields_for, los campos de función pensarán, es una opción hash (y no su objeto) y establece el objeto de formulario en nil. Esto significa que, aunque puede editar los valores, no aparecerán después de la edición. Puede ser un error o comportamiento inesperado de los rieles y tal vez arreglado en el futuro.

Sin embargo, por ahora, es bastante fácil hacerlo funcionar (aunque me tomó toda la mañana darme cuenta).

Puede dejar el modelo como está y en la vista debe dar campos para el objeto como una estructura abierta. Eso establecerá correctamente el objeto de registro (por lo que f2.object devolverá sus opciones) y en segundo lugar permite que el generador de text_field acceda al valor de su objeto/params.

Desde que incluí "|| {}", también funcionará con formularios nuevos/crear.

= form_for @widget do |f| 
    = f.fields_for :options, OpenStruct.new(f.object.options || {}) do |f2| 
    = f2.text_field :axis_y 
    = f2.text_field :axis_x 
    = f2.text_field :unit 

tener un gran día

+1

Bien hecho. Este consejo me ahorró algo de tiempo esta noche. No pensé en el hecho de que el objeto era un hash significaría que sería interpretado como un hash de opciones, pero si eso es lo que está sucediendo, tiene mucho sentido. – Legion

+1

Realmente me gusta esta solución, la he agregado a mi modelo para completarla: 'def options OpenStruct.new self [: options] || {} final', por lo que siempre obtienes OpenStruct al leer el atributo ... –

+2

Creo que hay un pequeño error: debes llamar a 'text_field' en' f2', no 'f' – Sidhannowe

4

He estado luchando con un problema muy similar. Las soluciones que encontré aquí fueron muy útiles para mí. Gracias @austinfromboston, @ Christian-Butske, @sbzoom y todos los demás. Sin embargo, creo que estas respuestas podrían estar un poco desactualizadas. Esto es lo que me funcionó con carriles 5 y Ruby 2.3:

En la forma:

<%= f.label :options %> 
<%= f.fields_for :options do |o| %> 
    <%= o.label :axis_y %> 
    <%= o.text_field :axis_y %> 
    <%= o.label :axis_x %> 
    <%= o.text_field :axis_x %> 
    ... 
<% end %> 

y luego en el controlador que tenía que actualizar los fuertes parámetros de este modo:

def widget_params 
    params.require(:widget).permit(:any, :regular, :parameters, :options => [:axis_y, :axis_x, ...]) 
end 

Se parece ser importante que el parámetro hash serializado se encuentre al final de la lista de parámetros. De lo contrario, Rails esperará que el siguiente parámetro sea también un hash serializado.

En la vista utilicé una lógica simple de si/entonces para mostrar solo el hash si no está vacío y luego mostrar solo pares clave/valor donde el valor no era nulo.

+0

Esto funciona para almacenar valores, pero cuando abre el formulario 'edit', no ve los valores que guardó –

+0

@YaroShm Ha pasado un tiempo desde que visité este problema. Como recuerdo, los parámetros fuertes fueron un poco complicados y las actualizaciones de los rieles en el último año pueden haber cambiado la sintaxis necesaria. Probablemente llevará una depuración cuidadosa y tal vez un poco de suerte para que funcione. Cuando lo hagas, publica el truco aquí. Ayudará a otros desarrolladores que encuentren esta publicación en el futuro. ¡La mejor de las suertes! – JDenman6

Cuestiones relacionadas