ruby-on-rails - formularios anidados rails
cómo evitar guardar registros vacíos en un formulario de carriles anidados (4)
Estoy usando la gema nested_form
para mi relación de AddressBook
. Cuando el usuario borra el valor de un Addr
existente, quiero eliminar ese Addr
lugar de guardarlo con un value
blanco
class Person < ActiveRecord::Base
has_many :addrs, dependent: :destroy
attr_accessible :name, :addrs_attributes
accepts_nested_attributes_for :addrs, reject_if: :addr_blank, allow_destroy: true
def addr_blank(a)
valid? && a[:id].blank? && a[:value].blank?
end
class Addr < ActiveRecord::Base
belongs_to :person
attr_accessible :kind, :label, :value, :person_id
Mi método :reject_if
funciona bien pero no me da todo lo que necesito
-
valid?
Mantiene mis Addrs en blanco alrededor a través de la validación. -
a[:id].blank?
evita los rechazos cuando el usuario se queda en blanco y el registro existente
Ahora, necesito eliminar (en lugar de guardar) un Addr
existente cuando el usuario borra el value
. Además, estoy exponiendo Personas y Addrs a través de una API RESTful. Veo dos opciones posibles:
-
params
hashparams
para agregar el mágico_destroy=1
param. IOW, emula la actividad del usuario de presionar el botón de eliminar. - Encapsule esto dentro del modelo de
Addr
manera que una actualización con unvalue
blanco se considere efectivamente una eliminación.
Basado en el consejo aquí es cómo lo implementé:
people_controller.rb
def update
@person = Person.find(params[:id])
@person.destroy_blank_addrs(params[:person])
respond_to do |format|
...
persona.rb
def destroy_blank_addrs(person_params)
if valid? && person_params[:addrs_attributes]
person_params[:addrs_attributes].each do |addr_params_array|
addr_params= addr_params_array[1]
addr_params[:_destroy] = ''1'' if !addr_params[:id].blank? && addr_params[:value].blank?
end
end
end
Una tercera alternativa sería agregar una devolución de llamada before_save
en Person que eliminará todas las direcciones que estén en blanco. Esta idea tiene algún mérito, pero probablemente no voy a seguir con ella.
De las dos opciones que presenta, no iré con el postprocesamiento de los parámetros. Funcionará, pero es demasiado trabajo. Además, el código del controlador se pondrá un poco más desordenado y creo firmemente en un controlador muy delgado.
La opción más fácil, en mi cabeza, es eliminar las direcciones en blanco después de guardar. Puede agregar la Person#remove_blank_addresses()
y luego llamarla para guardar con éxito. No necesita pasar los parámetros, ya que puede iterar las direcciones y eliminar las que están en blanco. Tiene la desventaja de crear direcciones vacías y luego destruirlas, pero de todos modos necesitarías eso para actualizar a las personas.
Si estamos hablando de la solución más limpia (en mi opinión), introduciría una tercera clase que manejaría toda esa lógica y dejaría que el controlador la delegara. El controlador sería bastante fácil de probar de forma aislada y luego puede escribir una especificación de modelo que verifique todos los detalles esenciales. Es un poco más de trabajo y no puedo pensar en un buen nombre ahora ( PersonUpdater
?), Pero podría ser una idea en la que valga la pena pensar.
accepts_nested_attributes_for :addrs,
allow_destroy: true,
:reject_if => proc { |att| att[:name].blank? && attr[:description].blank? }
accepts_nested_attributes_for :addrs,
allow_destroy: true,
reject_if: -> { |attr| [name, description].any? &:blank? }
accepts_nested_attributes_for :addrs,
allow_destroy: true,
reject_if: :all_blank