Подтвердить что ты не робот

Формы для создания и обновления полей массива Mongoid

Я пытаюсь создать форму для модели Mongoid, которая имеет поле массива. Я хочу, чтобы моя форма имела текстовое поле для каждой записи в массиве. Если я создаю новую запись, по умолчанию будет пустое поле (и некоторый javascript для динамического добавления новых полей на странице).

Я искал решение для решения с помощью полей_по_о_о_, но похоже, что он больше предназначен для обработки случая, когда у вас есть массив объектов/моделей, а не тот, который у меня есть, который представляет собой массив строк.

Я собираюсь использовать пример человека и номер телефона.

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 вместо этого

Есть ли у кого-нибудь лучшие предложения о том, как это реализовать? Или вы считаете это правильным?

4b9b3361

Ответ 1

Я согласен с вашими проблемами -

  • Жестко-кодированное имя поля в вызове text_field_tag.

  • Использование text_field_tag ​​вместо f.text_field

  • с помощью 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%>

В другом чистом подходе может быть задано текстовое поле form builder, а затем -

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

Здесь вы можете найти дополнительную информацию

Ответ 2

Вы можете работать с 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 %>

Ответ 3

Согласно комментарию, опубликованному mosch в комментарии к собственному решению:

всякий раз, когда вы используете fields_for, он ожидает объект с аксессуарами для атрибутов и некоторые другие методы, такие как new_record? В принципе говоря, шляпа объекта для реализации интерфейса ActiveModel.

Ответ на мой вопрос заключается в том, что нет лучшего способа, если я не создам еще одну модель для телефонных nubmers, как предположил mosch.

Ответ 4

Второй div должен выглядеть следующим образом

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