2011-01-31 12 views
78

¿Hay un método (o una forma de obtener una funcionalidad similar) para hacer un fields_for_with_index?Rieles: fields_for con índice?

Ejemplo:

<% f.fields_for_with_index :questions do |builder, index| %> 
    <%= render 'some_form', :f => builder, :i => index %> 
<% end %> 

ese ser parcial necesidades prestados a saber lo que el índice actual es en el bucle fields_for.

+3

que había + uno en los rieles principales ... Sigue encontrando un caso para este también. –

Respuesta

81

realidad, esto sería un mejor enfoque, siguiente documentación rieles más de cerca:

<% @questions.each.with_index do |question,index| %> 
    <% f.fields_for :questions, question do |fq| %> 
     # here you have both the 'question' object and the current 'index' 
    <% end %> 
<% end %> 

Desde: http://railsapi.com/doc/rails-v3.0.4/classes/ActionView/Helpers/FormHelper.html#M006456

También es posible especificar la instancia a utilizar:

<%= form_for @person do |person_form| %> 
    ... 
    <% @person.projects.each do |project| %> 
     <% if project.active? %> 
     <%= person_form.fields_for :projects, project do |project_fields| %> 
      Name: <%= project_fields.text_field :name %> 
     <% end %> 
     <% end %> 
    <% end %> 
    <% end %> 
+2

Ojalá hubiera algo integrado en fields_for para esto, pero como no se ha guardado tu respuesta mi día. Gracias. –

+0

También puede usar la opción no documentada: child_index en fields_for, si necesita más control sobre qué índice se representa, así: fields_for (: projects, project, child_index: index) –

+2

'@ questions.each_with_index' también funciona – Archonic

7

Pago Rendering a collection of partials. Si su requisito es que una plantilla necesita iterar sobre una matriz y renderizar una sub plantilla para cada uno de los elementos.

<%= f.fields_for @parent.children do |children_form| %> 
    <%= render :partial => 'children', :collection => @parent.children, 
     :locals => { :f => children_form } %> 
<% end %> 

Esto mostrará "_children.erb" y pasará la variable local "children" a la plantilla para su visualización. Un contador de iteraciones se pondrá automáticamente a disposición de la plantilla con un nombre del formulario partial_name_counter. En el caso del ejemplo anterior, la plantilla se alimentaría children_counter.

Espero que esto ayude.

+0

Obtengo "variable local indefinida o método 'question_comment_form_counter'" al hacer eso. 'question_comment_form' es mi nombre parcial ... – Shpigford

+0

Haz una pregunta has_many: comentarios y cómo estás creando comentarios, por ejemplo, en tu acción nueva o de edición de las preguntas, intenta hacer 1.times {question.comments.compilación} –

125

La respuesta es bastante simple ya que la solución se proporciona dentro de Rails. Puede usar los parámetros f.options. Así, dentro de su rendido _some_form.html.erb, Índice

se puede acceder por:

<%= f.options[:child_index] %> 

Usted no necesita hacer nada más.


Actualización: Parece que mi respuesta no era lo suficientemente claro ...

original del archivo HTML:

<!-- Main ERB File --> 
<% f.fields_for :questions do |builder| %> 
    <%= render 'some_form', :f => builder %> 
<% end %> 

rindió Sub-Forma:

<!-- _some_form.html.erb --> 
<%= f.options[:child_index] %> 
+11

¡Necesita más votos positivos! – Jeff

+8

no funciona aquí. ¿Puedes proporcionar el enlace de documentación de los rieles? –

+3

tampoco funciona para mí. – graphmeter

6

No veo una manera decente de hacer esto a través de los modos proporcionados por Rails, al menos no en -v3.2.14

@Sheharyar Naseer hace referencia al hash de opciones que se puede usar para resolver el problema, pero no tan lejos como puedo ver en la forma en que parece sugerir

Hice esto =>

<%= f.fields_for :blog_posts, {:index => 0} do |g| %> 
    <%= g.label :gallery_sets_id, "Position #{g.options[:index]}" %> 
    <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> 
    <%# g.options[:index] += 1 %> 
<% end %> 

o

<%= f.fields_for :blog_posts do |g| %> 
    <%= g.label :gallery_sets_id, "Position #{g.object_name.match(/(\d+)]/)[1]}" %> 
    <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> 
<% end %> 

En mi caso g.object_name devuelve una cadena como esta "gallery_set[blog_posts_attributes][2]" para el tercer campo rendí así que sólo coincidir con el índice en esta cadena y usarlo


en realidad una forma más frío (y tal vez más limpio?) Para hacerlo es pasar un lambda y lo llaman a incrementar.

# /controller.rb 
index = 0 
@incrementer = -> { index += 1} 

Y la en la vista

<%= f.fields_for :blog_posts do |g| %> 
    <%= g.label :gallery_sets_id, "Position #{@incrementer.call}" %> 
    <%= g.select :gallery_sets_id, @posts.collect { |p| [p.title, p.id] } %> 
<% end %> 
74

Como de Rails 4.0.2, un índice es ahora incluidos en el objeto FormBuilder:

http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormHelper/fields_for

Por ejemplo:

<%= form_for @person do |person_form| %> 
    ... 
    <%= person_form.fields_for :projects do |project_fields| %> 
    Project #<%= project_fields.index %> 
    ... 
    <% end %> 
    ... 
<% end %> 
+0

Esto funciona y es menos para escribir que 'project_fields.options [: child_index]'. ¡Así me gusta de esta manera mejor! – Casey

+0

Mucho mejor, esto debe ser aceptado. ¡Gracias! – gregblass

7

rieles 4+ y Rails 3 (con el parche de abajo)

<%= form_for @person do |person_form| %> 
    <%= person_form.fields_for :projects do |project_fields| %> 
    <%= project_fields.index %> 
    <% end %> 
<% end %> 

para los carriles de soporte 3

Para obtener f.index a trabajar en Rails 3 es necesario agregar un parche mono para sus proyectos inicializadores añadir esta funcionalidad a fields_for

# config/initializers/fields_for_index_patch.rb 

module ActionView 
    module Helpers 
    class FormBuilder 

     def index 
     @options[:index] || @options[:child_index] 
     end 

     def fields_for(record_name, record_object = nil, fields_options = {}, &block) 
     fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? 
     fields_options[:builder] ||= options[:builder] 
     fields_options[:parent_builder] = self 
     fields_options[:namespace] = options[:namespace] 

     case record_name 
      when String, Symbol 
      if nested_attributes_association?(record_name) 
       return fields_for_with_nested_attributes(record_name, record_object, fields_options, block) 
      end 
      else 
      record_object = record_name.is_a?(Array) ? record_name.last : record_name 
      record_name = ActiveModel::Naming.param_key(record_object) 
     end 

     index = if options.has_key?(:index) 
        options[:index] 
       elsif defined?(@auto_index) 
        self.object_name = @object_name.to_s.sub(/\[\]$/,"") 
        @auto_index 
       end 

     record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]" 
     fields_options[:child_index] = index 

     @template.fields_for(record_name, record_object, fields_options, &block) 
     end 

     def fields_for_with_nested_attributes(association_name, association, options, block) 
     name = "#{object_name}[#{association_name}_attributes]" 
     association = convert_to_model(association) 

     if association.respond_to?(:persisted?) 
      association = [association] if @object.send(association_name).is_a?(Array) 
     elsif !association.respond_to?(:to_ary) 
      association = @object.send(association_name) 
     end 

     if association.respond_to?(:to_ary) 
      explicit_child_index = options[:child_index] 
      output = ActiveSupport::SafeBuffer.new 
      association.each do |child| 
      options[:child_index] = nested_child_index(name) unless explicit_child_index 
      output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block) 
      end 
      output 
     elsif association 
      fields_for_nested_model(name, association, options, block) 
     end 
     end 

    end 
    end 
end 
0

Si usted quiere tener el control sobre los índices echa un vistazo a la index opción

<%= f.fields_for :other_things_attributes, @thing.other_things.build do |ff| %> 
    <%= ff.select :days, ['Mon', 'Tues', 'Wed'], index: 2 %> 
    <%= ff.hidden_field :special_attribute, 24, index: "boi" %> 
<%= end => 

Esto producirá

<select name="thing[other_things_attributes][2][days]" id="thing_other_things_attributes_7_days"> 
    <option value="Mon">Mon</option> 
    <option value="Tues">Tues</option> 
    <option value="Wed">Wed</option> 
</select> 
<input type="hidden" value="24" name="thing[other_things_attributes][boi][special_attribute]" id="thing_other_things_attributes_boi_special_attribute"> 

Si se envía el formulario, params incluirá algo así como

{ 
    "thing" => { 
    "other_things_attributes" => { 
    "2" => { 
     "days" => "Mon" 
    }, 
    "boi" => { 
     "special_attribute" => "24" 
    } 
    } 
} 

tuve que usar la opción de índice para conseguir mis múltiples menús desplegables trabajar. Buena suerte.

1

sé que esto es un poco tarde, pero recientemente he tenido que hacer esto se puede obtener el índice de la fields_for como esto

<% f.fields_for :questions do |builder| %> 
    <%= render 'some_form', :f => builder, :i => builder.options[:child_index] %> 
<% end %> 

espero que esto ayude :)

Cuestiones relacionadas