Как добавить дополнительные поля формы вне блока объявления формы - PullRequest
9 голосов
/ 08 февраля 2012

Основная цель: Разрешить плагинам, гемам добавлять дополнительные поля формы к предопределенным формам.

Например, приложение имеет форму входа в систему: <%= form_for(resource, :as => resource_name, ...) do |f| %> <%= devise_error_messages! %> ... <% end %>

Отдел маркетинга хочет начать кампанию в течение следующих 2 дней (например: зарегистрируйтесь с промо-кодом и получите X бонусных баллов). Поэтому нам нужно добавить дополнительное поле promo code ко ВСЕМ нашим регистрационным формам.

Есть ли способ добавить дополнительное поле в форму из моего rails-plugin / railtie и определить метод обратного вызова on_submit (для выполнения действий с моими дополнительными данными поля)?

Преимущества:

  • позволяет удалить функциональность за 2 дня или неделю, просто удалив ее из файла gem
  • гарантия того, что функциональность основного сайта не нарушена, он просто возвращается к исходной функциональности
  • гарантия того, что разработчик не оставил никакого кода где-либо в основном приложении
  • Плагин / Railtie заботится о сохранении / обновлении данных, которые ему принадлежат

Посмотрел код ActionView и, похоже, нет встроенного способа сделать это. Что ты думаешь?

ПРИМЕЧАНИЕ. Отличным примером является form_alter крючки Drupal.

Ответы [ 3 ]

2 голосов
/ 22 февраля 2012

Прежде всего, ваша идея выделить этот код в gem / railtie / engine превосходна. Я думаю, что вам лучше всего подойти к метке «form_for» и вставить дополнительное поле. Что касается триггера при отправке, если вы используете Rails 3.1 и используете конвейер ресурсов, вы также можете использовать javascript для gem serve, хотя для этого потребуется небольшое изменение в вашем application.js, чтобы потребовался файл js для gem, например require 'promo/application.js, если камень назывался «промо».

Посмотрите документы для Индивидуальный конструктор форм

Вот примерная идея, как это может работать, хотя я не пробовал этот код. Я бы поместил это в файл promo.rb, который подклассы Railtie или 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

В будущем, особенно если ваш отдел маркетинга может проводить больше кампаний, вы можете даже изменить формы приложения, указав на конкретного компоновщика, которого вы можете переопределить из самоцвета без внесения исправлений здесь.

1 голос
/ 17 февраля 2012

Идея в шагах:

1) Определить модель, такую ​​как AdditionalField (id, field_name, field_type, default_value, is_required)

2) затем создайте такую ​​функцию, как:

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) затем вы можете перебрать найденные дополнительные поля и при необходимости построить правильные типы полей.

Я использовал это решение для сайта сравнения, где им нужно было настроить вопросники для каждого отдельного типа сравнения.

Вот код рендеринга, который я использовал, вам нужно изменить его в соответствии с вашей ситуацией. отношения:

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

Контроллер

# 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 голосов
/ 24 февраля 2012

в этом случае должен быть создан новый гем, который -

  1. добавит промо-код в модель пользователя и сделает его доступным
  2. переопределить форму разработки для включения ввода промокода (просто скопируйте и вставьтепросмотреть форму и добавить промо-поле)
  3. Добавить необходимые проверки и дополнительную обработку (обратные вызовы) внутри пользовательской модели (метапрограммирование в пользовательском классе)

Мы не вносим никаких изменений в основнойприложение, следовательно, мы в безопасности.

...