ruby on rails - rails - Rieles: fields_for con índice?
rails form_with action (8)
A partir de Rails 4.0.2, ahora se incluye un índice 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 %>
¿Hay un método (o una forma de obtener una funcionalidad similar) para hacer un fields_for_with_index
?
Ejemplo:
<% f.fields_for_with_index :questions do |builder, index| %>
<%= render ''some_form'', :f => builder, :i => index %>
<% end %>
Esa fields_for
parcial necesita saber cuál es el índice actual en el bucle fields_for
.
En realidad, este sería un mejor enfoque, siguiendo más de cerca la documentación de Rails:
<% @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 %>
De: http://railsapi.com/doc/rails-v3.0.4/classes/ActionView/Helpers/FormHelper.html#M006456
También es posible especificar la instancia que se 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 %>
La respuesta es bastante simple ya que la solución se proporciona dentro de Rails. Puede usar parametros de f.options
. Entonces, dentro de su renderizado _some_form.html.erb
,
El índice se puede acceder por:
<%= f.options[:child_index] %>
No necesitas hacer nada más.
Actualización: parece que mi respuesta no fue lo suficientemente clara ...
Archivo HTML original:
<!-- Main ERB File -->
<% f.fields_for :questions do |builder| %>
<%= render ''some_form'', :f => builder %>
<% end %>
Renderizado Sub-Formulario:
<!-- _some_form.html.erb -->
<%= f.options[:child_index] %>
No puedo ver una manera decente de hacerlo 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 representado, por lo que solo "gallery_set[blog_posts_attributes][2]"
con el índice en esa cadena y lo uso.
En realidad, una manera más genial (y ¿quizás más limpia?) De hacerlo es pasar un lambda y llamarlo para incrementarlo.
# /controller.rb
index = 0
@incrementer = -> { index += 1}
Y el 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 %>
Pago y envío de una colección de parciales . 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 generará "_children.erb" y pasará la variable local "children" a la plantilla para mostrar. Un contador de iteración se pondrá automáticamente a disposición de la plantilla con un nombre de la forma partial_name_counter
. En el caso del ejemplo anterior, la plantilla se alimentaría con children_counter
.
Espero que esto ayude.
Sé que esto es un poco tarde, pero recientemente tuve que hacer esto para obtener el índice de los campos_ para así
<% f.fields_for :questions do |builder| %>
<%= render ''some_form'', :f => builder, :i => builder.options[:child_index] %>
<% end %>
Espero que esto ayude :)
Si desea tener control sobre los índices, consulte la opción de index
<%= 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, los parámetros incluirán algo así como
{
"thing" => {
"other_things_attributes" => {
"2" => {
"days" => "Mon"
},
"boi" => {
"special_attribute" => "24"
}
}
}
Tuve que usar la opción de índice para hacer que mis múltiples listas desplegables funcionaran. Buena suerte.
Rails 4+ y Rails 3 (con el parche a continuación)
<%= form_for @person do |person_form| %>
<%= person_form.fields_for :projects do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
Para el soporte de Rails 3
Para que f.index
funcione en Rails 3, debe agregar un parche mono a los inicializadores de sus proyectos para agregar 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