13

我一直在努力为具有数组字段的 Mongoid 模型创建一个表单。我希望我的表单在数组中的每个条目上都有文本框。如果我正在创建一条新记录,则默认值为一个空字段(以及一些用于在页面上动态添加新字段的 javascript)。

我已经搜索了使用 fields_for 的解决方案,但似乎更倾向于处理您拥有对象/模型数组的情况,而不是我拥有的情况,即字符串数组。

我将使用一个人和一个电话号码的例子。

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

对于控制器,假设是典型的控制器,但在new方法中我用一个空白字符串初始化了 phone_number 数组。

这是表单代码:

  <%= form_for(@person) do |f| %>
    <div class="field">
      <%= f.label :name %><br />
      <%= f.text_field :name %>
    </div>
    <div class="field">
      <%= f.label :phone_numbers %><br />
      <% @person.phone_numbers.each do |phone_number| %>
        <%= text_field_tag "person[phone_numbers][]", phone_number %>
      <% end %>
    </div>
  <% end %>

这一切都很好。有几件事我不喜欢。

  • text_field_tag 调用中字段的硬编码名称。
  • 使用 text_field_tag 而不是 f.text_field
  • 感觉我应该以某种方式使用 fields_for 而不是这个

有人对如何实现这一点有更好的建议吗?或者你会认为这是正确的吗?

4

4 回答 4

8

我同意你的担忧——

  1. text_field_tag调用中字段的硬编码名称。

  2. 使用 text_field_tag 而不是f.text_field

  3. 使用fields_for

在做了一些研究后发现前两个问题可以解决,第三个问题可能也可以但还没有尝试过。

 <%= form_for(@person) do |f| %>
  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :phone_numbers %><br />
    <% @person.phone_numbers.each do |phone_number| %>
      <%= f.text_field :phone_numbers, :name => "#{f.object_name}[phone_numbers][]"%>
    <% end %>
  </div>
<%end%>

另一种干净的方法可能是让表单构建器定义 text_field 然后 -

def text_field(attribute, *args)
  args.last.merge!(:name => "#{object_name}[#{attribute}][]") if args.last && args.last.is_a?(Hash) && args.last.delete(:array)
  super(attribute, args)
end

<% @person.phone_numbers.each do |phone_number| %>
  <%= f.text_field :phone_numbers, :array => true%>
<% end %>

您可以在这里找到更多信息

于 2012-01-19T19:02:00.447 回答
3

您可以使用 embeds_many:

class Person
  include Mongoid::Document
  field :name
  embeds_many :phone_numbers
end

class PhoneNumber
  include Mongoid::Document
  field :number
  embedded_in :person
end

然后,在您看来,您可以使用:

<%= form_for(@person) do |f| %>
  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <%= @person.phone_numbers.each do |phone_number| %>
    <%= f.fields_for phone_number do |p| %>
      <div class="field">
        <%= p.label :number %><br />
        <%= p.text_field :number %>
      </div>
    <% end %>
  <% end %>
<% end %>
于 2011-06-14T06:06:41.557 回答
1

根据 mosch 在对他自己的解决方案的评论中发布的评论:

每当您使用 fields_for 时,它都需要一个带有属性访问器的对象和一些其他方法,例如 new_record?基本上来说,实现ActiveModel接口的对象。

我的问题的答案是,除非我像 mosch 建议的那样为电话号码创建另一个模型,否则没有更好的方法。

于 2011-06-17T08:48:59.063 回答
0

第二个 div 应该如下所示

<div class="field"><%= f.fields_for :phone_numbers do | phone | %>
 <%= phone.text_field "phone_numer[]" %><% end %></div>
于 2012-11-28T09:23:41.627 回答