基于用户输入的动态表单和模型创建

时间:2012-05-17 14:00:08

标签: ruby-on-rails ruby-on-rails-3 forms model-view-controller mongoid

我希望有人能指导我做最好的方法。我正在设立一个注册表格供人们列出他们在各种工作中的技能/证书

Schema沿着这些方向使用Mongoid和Inheritance

class Person 
  include Mongoid::Document
  field :name, :type => String 
  field :education, :type => String 
end

class Accountant < Person
  field :cpa, :type => Boolean
  field :active_cpa, :type => Boolean  
end

class SoftwareDeveloper < Person
  field :full_stack, :type => Boolean
  field :language, :type => Array  
end

因此,用户将访问注册页面mysite.com/persons/new,其中将有一个表单来选择各种工作(按行业列出和分组:财务,工程,营销)并根据用户的选择,更详细的视图将显示特定于该工作职能。 (例如,软件开发人员可以列出编程语言,会计师可以列出他们是否有CPA)。

我的问题是,在控制器和视图层中处理此问题的最佳方法是什么?我正在覆盖大约100种不同的工作功能,所以我想将控制器和视图结合起来,而不是将用户重定向到mysite.com/accountants/new或mysite.com/engineers/new,并为每个功能提供控制器/视图具体工作。

有人可以帮助指导我找到最好的方法吗?我知道我将不得不使用jQuery和一些ajax调用来呈现详细的表单,但是我如何处理控制器因为form_for最初与用户绑定而且模型的属性有限所以我不知道如何得到参数并将它们传递给参数哈希,并对更具体的工作进行验证

1 个答案:

答案 0 :(得分:2)

我最近遇到过这样的问题。我最终使用基本类型的form_for设置我的表单,在你的情况下是Person。在表单中,我为基本字段创建了一个字段集,并使用单独的fieldset标记来表示每个子类中可用的额外字段,如下所示:

<%= form_for @person do |f| %>

<fieldset id="base">
  <%= f.label :name %>
  <%= f.check_box :name %>
  <!-- ... -->
</fieldset>

<fieldset id="accountant">
  <%= fields_for :accountant do |f| %>
     <%= f.label :cpa %>
     <%= f.check_box :cpa %>
     <!-- ... -->
  <% end %>
</fieldset>

<fieldset id="software_developer">
  <%= fields_for :software_developer do |f| %>
     <%= f.label :full_stack %>
     <%= f.check_box :full_stack %>
     <!-- ... -->
  <% end %>
</fieldset>

使用fields_for将导致POST形式在控制器中提供你的“创建”方法,如下所示:

params[:person] = { :name => "Joe Smith", ... }
params[:accountant] = { :cpa => true, ... }
params[:software_developer] = { :full_stack => false, ... }

在您的表单中,您还希望有一些方法来指定作业类型。就我而言,我使用了一个简单的选择标记:

<%= select_tag :job_type, options_for_select(@job_types) %>

这将为您提供控制器中的参数[:job_type]。从那里,“create”方法需要确定用户选择的作业类型,然后创建正确的子类并使用提供的参数。我用了一个案例陈述:

case params[:job_type]
when :accountant
  @person = Accountant.new(params[:person].merge(params[:accountant]))
when :software_developer
  @person = SoftwareDeveloper.new(params[:person].merge(params[:software_developer]))
when ...
end

如果你有很多子类,那可能不是很容易扩展。所以你可以做的是安排params [:job_type]为你提供要创建的类的名称作为表单提交的值。然后你可以做类似的事情:

@person = params[:job_type].constantize.new(params[:person].merge(params[params[:job_type].underscore])

此代码使用作业类型字段转换为类名,然后再次为子类查找正确的字段。

最后,当用户选择不同的作业类型时,您将需要使用一些jQuery来隐藏和显示不同的字段集。为此,将.change()处理程序附加到select_tag元素并使其隐藏所有字段集,然后显示一个字段集,其“id”与所选作业类型匹配。如果您需要示例代码,请告诉我。

作为画龙点睛,您可能会考虑在局部渲染特定于作业的字段集,以便您的主窗体只有一系列调用来呈现所有不同类型的作业。

我不知道是否有更好的方法可以做到这一点,但这种方法对我来说真的很棒,并允许我有一个控制器和一组视图来管理整个父/子子类的创建和编辑树。