2012-02-08 29 views
9

El objetivo principal: Permitir complementos, gemas para agregar campos de formulario adicionales a formularios predefinidos.Cómo agregar campos de formulario adicionales fuera del bloque de declaración de formulario

Por ejemplo, la aplicación tiene un formulario de acceso legado:

<%= form_for(resource, :as => resource_name, ...) do |f| %> 
    <%= devise_error_messages! %> 
    ... 
<% end %>

departamento de marketing quiere empezar una campaña para los próximos 2 días (por ejemplo: cuando se registra en un código promocional y obtener puntos de bonificación X). Entonces, necesitamos agregar un campo promo code adicional a TODOS nuestros formularios de registro.

¿Hay alguna manera de agregar un campo adicional al formulario de mis rails-plugin/railtie, y definir un método de devolución de llamada on_submit (para tomar medidas en mis datos de campo adicionales)?

Beneficios:

  • que permite la eliminación de la funcionalidad en 2 días o una semana, simplemente quitándolo de archivo joya
  • garantía de que la funcionalidad del sitio central no está roto, simplemente se cae de nuevo a la Funcionalidad original
  • garantiza que un desarrollador no ha dejado ningún código en alguna parte de la aplicación principal
  • plugin/railtie se ocupa de los datos de guardado/actualización que le pertenecen

Miró el código de ActionView, y parece que no hay una forma integrada de hacerlo. ¿Cuáles son tus pensamientos?

NOTA: Los ganchos form_alter de Drupal son un gran ejemplo.

Respuesta

1

Idea en pasos:

1) Definir un modelo como AdditionalField (id, nombre_campo, field_type, default_value, is_required)

2) a continuación, crear una función como:

def self.for_form(my_form_name = nil) 
if my_form_name.nil? 
    self.all 
else 
    self.find(:all, :contitions => {:form_type => my_form_name.type} # or whatever selection criteria 
end 

3) luego puede iterar sobre los AdditionalFields encontrados y construir los tipos de campo correctos según sea necesario.

Utilicé esta solución para un sitio web de comparación donde necesitaban configurar los cuestionarios para cada tipo de comparación diferente.

Aquí está el código de renderizado que utilicé, deberá enmendarlo para adaptarlo a su situación. relaciones son:

convention -<booking>- user 
convention -< convention_question 
booking -< guests 
guest -< guest_answers 

QuestionsHelper

def render_guest_questions(guest, convention_question)  

    fields_for "booking[guest_answer_attributes][]", convention_question do |m| 
     case convention_question.display_type 
     when "Text" 
     '<td>' + text_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]") + '</td>' 
     when "Boolean" 
     '<td>' + hidden_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "No") + check_box_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "Yes") + '</td>' 
     end 
    end 

end 

controlador

# TURN GUEST/QUESTIONS INTO guest answers 
if params[:booking] && !params[:booking].blank? && !params[:booking][:guest_answer_attributes].blank? 
    params[:booking][:guest_answer_attributes].each do |k,v| 
     handle_answers(k, v) 
    end 
end 

def handle_answers(k, v) 
    x = k.mb_chars.split(/_/) 
    g_id = x[2] 
    q_id = x[3] 
    item = GuestAnswer.find_or_create_by_guest_id_and_convention_question_id(
        {:guest_id => g_id, 
        :convention_question_id => q_id, 
        :answer => v}) 
end 
+0

¿Cómo almacenaste los datos enviados? – Uzbekjon

+0

Lo guardé creando una tabla user_answers. 'UserAnswer' que tiene' belongs_to: user; belongs_to AdditionalField; 'con los campos de' user_id, additional_field_id, answer_value' – TomDunning

+0

uzbekjon - Agregué el código que utilicé para el proyecto que hice. Siéntase libre de preguntar más si no tiene sentido. – TomDunning

2

En primer lugar, su idea de aislar este código en una joya/railtie/motor es excelente. Creo que tu mejor opción es aplicar el parche de mono al método form_for y quedarte en el campo extra. En cuanto al activador de envío, si estás en Rails 3.1 y están usando la canalización de activos, puede hacer que la gema sirva también javascript, aunque eso requeriría un pequeño cambio en su application.js para requerir el archivo js de la gema, p. require 'promo/application.js, si la gema se llamara "promo".

Tome un vistazo a la documentación de Customized Form Builder

He aquí alguna idea aproximada de cómo esto podría funcionar, aunque no he probado este código. Me gustaría poner esto en el archivo promo.rb que sub-classes Railtie o Engine.

ActiveSupport.on_load(:action_view) do 

    module ActionView 
    module Helpers 
     module FormHelper 
     extend ActiveSupport::Concern 

     included do 
      alias_method_chain :form_for, :promo_code 
     end 

     module InstanceMethods 

      def form_for_with_promo_code(record, options = {}, &proc) 
      output = form_for_without_promo_code(record, options.merge(builder: FormBuilderWithPromoCode), proc) 
      # See file: actionpack-3.1.3/lib/action_view/helpers/form_helper.rb for details 
      # the output will have "</form>" as the last thing, strip that off here and inject your input field 
      promo_field = content_tag :input, name: 'promo_code' # you can also run this through the proc if you want access to the object 
      output.sub(%r{</form>$},promo_field+'</form>') 
      end 
     end 
     end 
    end 
    end 
end 

Down the road, esp. si su departamento de marketing puede ejecutar más campañas, puede incluso querer cambiar los formularios de la aplicación, para apuntar a un generador específico que puede anular de una gema sin el parche de monos aquí.

+0

Gracias por señalar en la dirección "Formador de formularios personalizado". Tu respuesta es la más cercana a lo que quiero. Creo que escribiré un generador de formularios personalizado que agregue campo oculto (similar en idea a "_method") y agregue un middleware de rack que busque ese campo y active métodos adicionales en mis motores/complementos. – Uzbekjon

+0

Profundizando en el código de Rails Encontré el método 'extra_tags_for_form' que agrega etiquetas' authenticity_token'. Presentaré una solicitud de extracción que permitirá que otros métodos agreguen etiquetas adicionales. Pero tengo la sensación de que esto ya debería haberlo hecho personas más inteligentes que yo. ¿Hay forma de agregar una etiqueta a todos los formularios (de la forma en que lo hace 'protect_from_forgery')? – Uzbekjon

+0

@Uzbekjon Tuve el mismo pensamiento, pero no pude encontrar un gancho para eso. Estaba pensando en hackear fields_for para hacer esto, pero no era obvio que esto no tendría efectos secundarios. Estoy tan sorprendido como tú de que esto no se haya hecho. También verifiqué el código 'simple_form', pero simplemente envolví los helpers de formularios nativos y configuré su propia API. –

0

en este caso, la nueva joya debe ser creado que lo hará -

  1. complemento promo_code al modelo de usuario y hacerla accesible forma legado
  2. anulación de incorporar entrada de código promocional (simplemente copiar y pegar la vista de formulario y añadir promo campo)
  3. Añadir validaciones necesarias & procesamiento adicional (devoluciones de llamada) en el interior modelo de usuario (meta-programación en la clase de usuario)

no somos d Al hacer cualquier cambio dentro de la aplicación principal, estamos seguros.

+0

Sí, este método funcionaría bien si solo tiene un "cambio". Pero, ¿qué sucede si tienes más de un complemento que quiere cambiar el formulario? El método implica que los desarrolladores deberán realizar un seguimiento de todas las gemas y complementos que actualmente están cambiando el formulario. – Uzbekjon

+0

Se debe llevar un seguimiento de los motores externos y lo que están haciendo mientras se realizan más cambios. –

Cuestiones relacionadas