Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forms to create and update Mongoid array fields

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?

like image 231
Dave Jensen Avatar asked Jun 14 '11 04:06

Dave Jensen


1 Answers

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

like image 107
Sandip Ransing Avatar answered Sep 26 '22 06:09

Sandip Ransing