主要目标:允许插件,宝石向预定义表单添加其他表单字段。
例如,应用程序有一个设计登录表单:
<%= form_for(resource, :as => resource_name, ...) do |f| %>
<%= devise_error_messages! %>
...
<% end %>
营销部门希望在接下来的两天内开始一项活动(例如:使用促销代码注册并获得X奖励积分)。因此,我们需要在所有注册表单中添加一个promo code
字段。
有没有办法从我的rails-plugin / railtie向表单添加额外的字段,并定义on_submit
回调方法(对我的其他字段数据采取措施)?
优点:
看看ActionView代码,似乎没有内置的方法。你有什么想法?
注意:Drupal的form_alter
挂钩就是一个很好的例子。
答案 0 :(得分:2)
首先,您在gem / railtie / engine中隔离此代码的想法非常好。我认为你最好的选择可能是修补form_for方法,并坚持使用额外的字段。关于on-submit触发器,如果您使用的是Rails 3.1并且正在使用资产管道,那么您也可以让gem服务javascript,尽管这需要您的application.js
进行一些小的更改以要求gem的js文件,例如require 'promo/application.js
,如果宝石被称为“促销”。
虽然我没有尝试过这段代码,但这里有一些粗略的想法。我将它放在子类Railtie
或Engine
的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)
在这种情况下,应该创建新的宝石 -
我们在主要应用程序中没有做任何更改,因此我们是安全的。