Existe-t-il une méthode (ou un moyen de retirer une fonctionnalité similaire) pour créer un fields_for_with_index
?
Exemple:
<% f.fields_for_with_index :questions do |builder, index| %>
<%= render 'some_form', :f => builder, :i => index %>
<% end %>
Ce rendu partiel doit savoir quel est l'index actuel dans la boucle fields_for
.
Ce serait en fait une meilleure approche, en suivant de plus près la documentation 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
Il est également possible de spécifier le exemple à utiliser:
<%= 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 réponse est assez simple car la solution est fournie dans Rails. Vous pouvez utiliser f.options
params. Donc, dans votre rendu _some_form.html.erb
,
Index peut être consulté par:
<%= f.options[:child_index] %>
Vous n'avez rien d'autre à faire.
Mise à jour: Il semble que ma réponse n'était pas assez claire ...
Fichier HTML original:
<!-- Main ERB File -->
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder %>
<% end %>
Sous-formulaire rendu:
<!-- _some_form.html.erb -->
<%= f.options[:child_index] %>
A partir de Rails 4.0.2, un index est maintenant inclus dans l'objet FormBuilder:
http://apidock.com/Rails/v4.0.2/ActionView/Helpers/FormHelper/fields_for
Par exemple:
<%= form_for @person do |person_form| %>
...
<%= person_form.fields_for :projects do |project_fields| %>
Project #<%= project_fields.index %>
...
<% end %>
...
<% end %>
Pour Rails 4+
<%= form_for @person do |person_form| %>
<%= person_form.fields_for :projects do |project_fields| %>
<%= project_fields.index %>
<% end %>
<% end %>
Monkey Patch pour Rails 3 Support
Pour que f.index
fonctionne dans Rails 3, vous devez ajouter un patch monkey aux initialiseurs de vos projets pour ajouter cette fonctionnalité à 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
Commander Rendre une collection de partiels . Si votre exigence est qu'un modèle doit parcourir un tableau et générer un sous-modèle pour chacun des éléments.
<%= f.fields_for @parent.children do |children_form| %>
<%= render :partial => 'children', :collection => @parent.children,
:locals => { :f => children_form } %>
<% end %>
Cela rendra "_children.erb" et transmettra la variable locale "enfants" au modèle pour l'affichage. Un compteur d'itérations sera automatiquement mis à la disposition du modèle avec un nom de la forme partial_name_counter
. Dans le cas de l'exemple ci-dessus, le modèle serait alimenté children_counter
.
J'espère que cela t'aides.
Je ne vois pas comment faire de manière décente à travers les méthodes fournies par Rails, du moins pas dans -v3.2.14.
@ Sheharyar Naseer fait référence au hachage des options qui peut être utilisé pour résoudre le problème, mais pas aussi loin que je puisse le voir dans la façon dont il semble le suggérer.
J'ai fait ceci =>
<%= 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 %>
ou
<%= 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 %>
Dans mon cas, g.object_name
renvoie une chaîne telle que celle-ci "gallery_set[blog_posts_attributes][2]"
pour le troisième champ affiché, je ne fais donc que correspondre à l'index de cette chaîne et l'utiliser.
En fait, une façon plus froide (et peut-être plus propre?) De le faire est de passer un lambda et de l'appeler pour augmenter.
# /controller.rb
index = 0
@incrementer = -> { index += 1}
Et le dans la vue
<%= 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 %>
Je sais que c'est un peu tard, mais j'ai récemment dû le faire, vous pouvez obtenir l'index des champs_pour ceci
<% f.fields_for :questions do |builder| %>
<%= render 'some_form', :f => builder, :i => builder.options[:child_index] %>
<% end %>
J'espère que ca aide :)
Si vous souhaitez contrôler les index, cochez l’option 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 =>
Cela produira
<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 le formulaire est soumis, params inclura quelque chose comme
{
"thing" => {
"other_things_attributes" => {
"2" => {
"days" => "Mon"
},
"boi" => {
"special_attribute" => "24"
}
}
}
J'ai dû utiliser l'option index pour que mes multi-menus déroulants fonctionnent. Bonne chance.