ruby on rails - tag - ¿Cómo editar un campo serializado de Rails en un formulario?
formularios en ruby on rails (7)
Tengo un modelo de datos en mi proyecto Rails que tiene un campo serializado:
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 que un usuario edite 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 simplemente se enviarán nuevamente como una cadena.
¿Cuál es la mejor / manera correcta de editar un campo hash serializado como este en Rails?
Bueno, tuve el mismo problema e intenté 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.
Como 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
Que tengas un gran día
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 funcionó para mí con Rails 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 tuve que actualizar los parámetros fuertes de esta manera:
def widget_params
params.require(:widget).permit(:any, :regular, :parameters, :options => [:axis_y, :axis_x, ...])
end
Parece 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 solo mostrar pares de clave / valor donde el valor no era nulo.
Intento hacer algo similar y encontré este tipo de trabajos:
<%= 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 bien y puedo:
@search = Search.new(params[:search])
entonces su "mitad" funciona ...
No necesita setter / getters, acabo de definir en el modelo:
serialize :content_hash, Hash
Luego en la vista, lo hago (con simple_form, pero similar con vanilla Rails):
= 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 último problema es cómo permitir que el usuario agregue un nuevo par clave / valor.
Si sabe cuáles van a ser las teclas de opción por adelantado, puede declarar getters y setters especiales para ellas de la siguiente manera:
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 matriz de opciones como atributos, lo que le permite usar los Rails como ayudantes de esta manera:
#haml
- form_for @widget do |f|
= f.text_field :axis_y
= f.text_field :axis_x
= f.text_field :unit
Sugeriré algo simple, porque todo el tiempo, cuando el usuario guardará la forma, obtendrá una cadena. Entonces, puede usar, por ejemplo, antes de filtrar y analizar esos datos de esa manera:
before_save do
widget.options = YAML.parse(widget.options).to_ruby
end
por supuesto, debe agregar validación si es correcto YAML. Pero debería funcionar.
emh está 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í está 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 que agregue en "fields_for" aparecerá 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.