如何在表单声明块之外添加额外的表单字段

时间:2012-02-08 16:12:05

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-plugins

主要目标:允许插件,宝石向预定义表单添加其他表单字段。

例如,应用程序有一个设计登录表单:

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

营销部门希望在接下来的两天内开始一项活动(例如:使用促销代码注册并获得X奖励积分)。因此,我们需要在所有注册表单中添加一个promo code字段。

有没有办法从我的rails-plugin / railtie向表单添加额外的字段,并定义on_submit回调方法(对我的其他字段数据采取措施)?

优点:

  • 它允许在2天或一周内删除功能,只需将其从gem文件中删除
  • 即可
  • 保证核心网站的功能不被破坏,它只是回归原始功能
  • 保证开发人员未在主应用程序的某处留下任何代码
  • plugin / railtie负责保存/更新属于它的数据

看看ActionView代码,似乎没有内置的方法。你有什么想法?

注意:Drupal的form_alter挂钩就是一个很好的例子。

3 个答案:

答案 0 :(得分:2)

首先,您在gem / railtie / engine中隔离此代码的想法非常好。我认为你最好的选择可能是修补form_for方法,并坚持使用额外的字段。关于on-submit触发器,如果​​您使用的是Rails 3.1并且正在使用资产管道,那么您也可以让gem服务javascript,尽管这需要您的application.js进行一些小的更改以要求gem的js文件,例如require 'promo/application.js,如果宝石被称为“促销”。

查看Customized Form Builder

的文档

虽然我没有尝试过这段代码,但这里有一些粗略的想法。我将它放在子类RailtieEngine的promo.rb文件中。

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

在路上,特别是如果你的营销部门可以运行更多的广告系列,你甚至可能想要更改应用程序的表单,指向一个特定的构建器,你可以在没有猴子修补的情况下从gem中覆盖它。

答案 1 :(得分:1)

步骤中的想法:

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)然后您可以迭代找到的AdditionalFields并根据需要构建正确的字段类型。

我将此解决方案用于比较网站,他们需要为每种不同的比较类型配置问卷。

这是我使用的渲染代码,你需要修改它以适应你的情况。 关系是:

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

答案 2 :(得分:0)

在这种情况下,应该创建新的宝石 -

  1. 将promo_code添加到用户模型并使其可访问
  2. 覆盖设计表单以合并促销代码输入(只需复制粘贴视图表单并添加促销字段)
  3. 添加必要的验证&amp;用户模型内的额外处理(回调)(用户类的元编程)
  4. 我们在主要应用程序中没有做任何更改,因此我们是安全的。