创建和更新 Mongoid 数组字段的表单

发布于 2024-11-15 10:24:44 字数 1135 浏览 6 评论 0原文

我一直在努力为具有数组字段的 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 而不是 this

有人对如何实现这个有更好的建议吗?或者您认为这是正确的吗?

I've been struggling to create a form for a Mongoid model that has an array field. I want my form to have on text box per entry in the array. If I'm creating a new record, the default will be one empty field (and some javascript to add new fields dynamically on the page).

I've searched around for a solution using fields_for but it seems that is more intended to handle the case where you have an array of objects/models and not the case I have, which is an array of strings.

I'm going to use the example of a person and a phone number.

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

For the controller, just assume the typical controller but in the new method I initialized the phone_number array with one blank string.

Here's the form code:

  <%= 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 %>

This all works fine. There are a few things that I don't like.

  • The hardcoded name of the field in the text_field_tag call.
  • Using text_field_tag instead of f.text_field
  • Having the feeling like I should somehow be using fields_for instead of this

Does anybody have any better suggestions on how to implement this? Or would you consider this correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

〃温暖了心ぐ 2024-11-22 10:24:44

我同意您的担忧 -

  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 %>

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

I agree with your concerns -

  1. The hard-coded name of the field in the text_field_tag call.

  2. Using text_field_tag instead of f.text_field

  3. using fields_for

After doing some research found that first two concerns can be solved and probably also third can but haven't tried yet.

 <%= 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%>

Another clean approach could be having form builder defined text_field and then having -

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 %>

You can find more information here

樱娆 2024-11-22 10:24:44

您可以使用 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 %>

You could work with 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

And then, within your view, you could use:

<%= 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 %>
两相知 2024-11-22 10:24:44

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

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

我的问题的答案是,没有更好的方法,除非我为电话数字创建另一个模型,就像莫什建议的那样。

According to a comment posted mosch in a comment to his own solution:

whenever you use fields_for, it expects an object with accessors for the attributes and some other methods like new_record? Basically speaking, the object hat to implement the ActiveModel interface.

The answer to my question is that there is not a better way unless I create another model for the phone nubmers, like was mosch suggested.

再可℃爱ぅ一点好了 2024-11-22 10:24:44

第二个 div 应该如下所示

<div class="field"><%= f.fields_for :phone_numbers do | phone | %>
 <%= phone.text_field "phone_numer[]" %><% end %></div>

The second div should be like the following

<div class="field"><%= f.fields_for :phone_numbers do | phone | %>
 <%= phone.text_field "phone_numer[]" %><% end %></div>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文