Формы для создания и обновления полей массива 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 вместо этого
Есть ли у кого-нибудь лучшие предложения о том, как это реализовать? Или вы считаете это правильным?
Ответы
Ответ 1
Я согласен с вашими проблемами -
После нескольких исследований выяснилось, что первые две проблемы могут быть решены, и, возможно, третье возможно, но еще не попытались.
<%= 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>