2011-06-14 12 views
13

He estado luchando para crear un formulario para un modelo Mongoid que tiene un campo de matriz. Quiero que mi formulario tenga en el cuadro de texto por entrada en la matriz. Si estoy creando un nuevo registro, el valor predeterminado será un campo vacío (y algunos javascript para agregar nuevos campos dinámicamente en la página).Formularios para crear y actualizar campos mongoid array

He buscado una solución usando fields_for, pero parece que está más destinado a manejar el caso donde tienes una matriz de objetos/modelos y no el caso que tengo, que es una matriz de cadenas.

Voy a usar el ejemplo de una persona y un número de teléfono.

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

Para el controlador, a suponer el controlador típico, pero en el método new I inicializa la matriz phone_number con una cadena en blanco.

Aquí está el código del formulario:

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

Todo esto funciona bien. Hay algunas cosas que no me gustan.

  • El nombre codificado del campo en la llamada text_field_tag.
  • Usando text_field_tag ​​en lugar de f.text_field
  • Tener la sensación de que de alguna manera debería usar fields_for en lugar de esta

¿Alguien tiene alguna sugerencia mejor sobre cómo implementar esto? ¿O lo considerarías correcto?

Respuesta

8

Estoy de acuerdo con sus preocupaciones -

  1. El nombre codificado del campo en la llamada text_field_tag.

  2. Usando text_field_tag ​​en lugar de f.text_field

  3. usando fields_for

Después de hacer algunas investigaciones encontraron que los dos primeros problemas se pueden resolver y probablemente también la tercera lata, pero no han probado todavía.

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

Otro enfoque limpio podría estar teniendo constructor de la forma text_field y luego tener definido -

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

+1

En el primer ejemplo es: 'f.text_field: numeros_telefono,: nombre => "# {} f.object_name [numeros_telefono] []"' ¿Se puede cambiar este typo – shingara

+1

Esta solución está bien, pero también hay que establecer el valor de cada número de teléfono: En su código, cada una 'numeros_telefono [] campo' '@ tiene el person.phone_numbers' arsenal completo como valor. Tienes que cambiar a esto: 'f.text_field: phone_numbers,: name =>" # {f.object_name} [phone_numbers] [] ",: value => phone_number'. En mi caso, ahora tengo que agregar nuevos campos con cocoon, que seguramente será otro problema ya que está basado en asociaciones ... –

3

Se podría trabajar con 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 

Y luego, dentro de su punto de vista, se puede utilizar:

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

estoy en realidad tratando de evitar esto. Esta sería una solución aceptable si el modelo PhoneNumber tenía más de un campo. El ejemplo que elegí fue la analogía más cercana sin confundir las cosas. En mi situación no habría necesidad de tener un modelo PhoneNumber adicional, incluso si estuviera incrustado. –

+1

Entiendo. Sin embargo, cada vez que use fields_for, ¿espera un objeto con acceso para los atributos y algunos otros métodos como new_record? Básicamente, el sombrero de objetos para implementar la interfaz ActiveModel. – moritz

+0

Gracias, dado que este es el caso, no puedo usar fields_for en este escenario. –

1

De acuerdo con un comentario publicado mosch en un comentario a su propia solución:

siempre que use fields_for, ¿espera un objeto con acceso para los atributos y algunos otros métodos como new_record? Básicamente, el sombrero de objetos para implementar la interfaz ActiveModel.

La respuesta a mi pregunta es que no hay una mejor manera a menos que cree otro modelo para los nubmers del teléfono, como se sugirió mosch.

0

El segundo div debe ser como la siguiente

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