我想介绍一个非常有用的rails工具, nested_form,它使编写嵌入式表单变得非常容易。
首先,为了保证代码的简洁和干净(dry), rails提供了fields_for和accepted_nested_attributes_for .通过使用这2个工具,可以在一个表单中嵌入多个关连的模型,而不需要修改controller的方法。例如:
有2个关联的模型, Company 和 Branch。
ClassCompany < ActiveRecord::Base
attr_accessible :name,branches_attributes
has_many:braches
accepted_nested_attributes_for:branches
end
class Branch < ActiveRecord::Base
attr_accessible:name,address,company_id
belongs_to :company
end
我们可以在company的_form.html.erb的文件中增加几行,实现在创建或修改company时,修改branch.
<%= form_for @company do |f| >
<%=f.label :name%><br/>
<%=f.text_field:name %><br/>
<%=f.fields_for:branches do |branch| %>
<%=branch.label :name %><br/>
<%=branch.text_field :name %><br/>
<%=branch.label :address %> <br/>
<%=branch.text_field :address %><br/>
<% end %>
<%=f.submit%>
<% end %>
以上代码可以实现在创建一个公司时,同时设定他的一个分支。
在使用fields_for和accepted_nested_attributes_for时有几个地方必须注意,否则,你将无法实现你的目标。
首先,你需要在需要嵌套的模型内,添加
accepted_nested_attributes_for :model_names
model_names是你需要嵌入的模型名(与has_many句中的模型名相同)
同时,你必须在attr_accessible句中添加model_names_attributes;注意,这里添加的不是模型名,是模型名加上”_attributes”。这是由于fields_for将使表单生成一个名为model_names_attributes的项,并且accepted_nested_attributes_for生成同名的方法。这样你在创建,或改模型时,不用修改controller的方法就可以实现同时创建模型和该嵌入模型关联的模型。
另一个需要注意的是,在创建模型时,由于新的模型实例中并没有子模型,这将导致fields_for的子表单无法显示出来。其中一个解决办法是修改new的方法,以company为例,new的方法可以是这样的:
defnew
@company=Company.new
@company.branches.build
respond_with @company
end
这样,就可以解决在创建时,子表单无法显示的问题。
那么你可能发现了新的问题了,现在company的表单中只能有一个或者固定数量的子表单。但在实际使用中,通常子表单的数量通常是不固定的,需要动态增减的,如何处理呢?
一种方案是可以先隐藏一个子表单,然后在需要时用javascripts将其复制表单中。这种方法有一些问题。主要是子表单的ID重名的问题。ID重名的问题将在你使用javascript捕捉子表单的事件时发生。如你需要使用jquery的datepickers。
为了解决上面的问题,并使子表单的问题变简单, 建议使用nested_form.
只需要添加 gem ‘nested_form’到你的Gemfile然后运行 bundle install.你就会有一个nested_form_for帮助方法了。
nested_form_for使嵌套子表单变得非常简单。首先,你不必修改new的方法,在表单中点击’add’将会增加一个子表单,‘remove’将会移走一个子表单。将表单提交后,被删除的子表单对应的实例将被删除。如果允许删除已保存在数据库中子表单,accepted_nested_attributes_for句中需要增加:allow_destroy=>true的选项。不需要修改controller的方法。
使用nested_form_for,在表单中添加<%=f.link_to_add ‘add’ %>将实现子表单的动态增加;
在子表单中添加<%=branch.link_to_remove ‘remove’ %>将实现子表单的动态删除;
如果需要使用datepicker之类的javascipt应用,点击<%=f.link_to_add 'add' %> 将会产生一个nested:fieldAdded事件,捕捉这个事件,可以实现在新增加的子表单中,实现datepicker功能。需要注意的是,该事件是从<%=f.link_to_add‘add’%>行产生的,不是从子表单中产生的。
就这样,非常简单就实现了嵌入表单。可以动态的增加,减少。很少的代码,简单, 漂亮。如果需要更多的信息,可以访问nested_from的主页https://github.com/ryanb/nested_form