Rails:fields_for with index?


102

を行う方法(または同様の機能を引き出す方法)はありfields_for_with_indexますか?

例:

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

レンダリングされるその部分は、現在のインデックスがfields_forループにあるものを知る必要があります。


3
私はこれをコアレールに+ 1つ入れます...この1つについてもケースに遭遇し続けます。
Nathan Bertram

回答:


92

これは実際にはより良いアプローチであり、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 %>

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

使用するインスタンスを指定することもできます:

  <%= 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
このためにfields_forに何かが組み込まれているといいのですが、あなたの答えがないので、私の日は救われました。ありがとう。
Anders Kindberg、2012

1
次のように、レンダリングするインデックスをより詳細に制御する必要がある場合は、フィールド化されていないオプション:child_indexをfields_forで使用することもできます。fields_for(:projects、project、child_index:index)
Anders Kindberg

これは実用的なRails APIリンクapi.rubyonrails.org/classes/ActionView/Helpers/…です
Archonic

7
Rails 4.0.2+ユーザーは、インデックスがビルダーに組み込まれているので、Benの答えを確認する必要があります。
notapatch

1
@マルコあなたは王様です。あなたは私の毎日を救った。:-)私はあなたに10+以上のカップルを与えることができればいいのに...!

157

ソリューションはRails内で提供されるため、答えは非常に簡単です。f.optionsparams を使用できます。したがって、レンダリングされた内_some_form.html.erb

インデックスには次の方法でアクセスできます。

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

他に何もする必要はありません。


更新:私の答えは十分に明確ではなかったようです...

元のHTMLファイル:

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

レンダリングされたサブフォーム:

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

10
ここでは機能しません。Railsのドキュメントリンクを提供できますか?
Lucas Renan

2
@LucasRenan&@graphmeter-もう一度質問を読ん<%= f.options[:child_index] %>でくださいbuilder。元のintではなく、レンダリングされたサブフォーム(この場合は_some_form.html.erb)を呼び出す必要があります。より明確にするために回答を更新しました。
Sheharyar 2013年

1
奇数は、私が得るnilことのために
bcackerman

1
@SheharyarこれはRails 4で機能しますが、 '1450681048049,1450681050158,1450681056830,1450681141951,1450681219262'のような値が得られます。しかし、私はこの形式のインデックスが必要です '1,2,3,4,5'。私は何をすべきか?
ヴィダル

2
鮮やかさ。これは絶対に受け入れられる答えです。
jeffdill2

100

Rails 4.0.2以降、インデックスはFormBuilderオブジェクトに含まれるようになりました。

https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-fields_for

例えば:

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

これは機能し、を書くよりも少ないですproject_fields.options[:child_index]。だから私はこの方が好きです!
ケーシー

16

Rails 4以降の場合

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

Rails 3サポートのモンキーパッチ

取得するにはf.indexRailsの3で動作するように、あなたはこの機能を追加するために、プロジェクトの初期化子にモンキーパッチを追加する必要があります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

これまでのところより良い答え、使用.indexははるかにきれいです。
Lucas Andrade

7

チェックアウトパーシャルのコレクションをレンダリングする。テンプレートが配列を反復処理し、各要素のサブテンプレートをレンダリングする必要があるという要件がある場合。

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

これにより、「_ children.erb」がレンダリングされ、ローカル変数「children」がテンプレートに渡されて表示されます。反復カウンターは、フォームの名前とともにテンプレートで自動的に使用できるようになりますpartial_name_counter。上記の例の場合、テンプレートは供給されchildren_counterます。

お役に立てれば。


それを行うと、「未定義のローカル変数またはメソッド 'question_comment_form_counter'」が表示されます。question_comment_form私の部分的な名前です...
Shpigford 2011年

実行(Do)質問にhas_many:コメントは、どのようにあなたが質問の新しいまたは編集アクションの例のコメントを構築しているが、1×{question.comments.build}をやってみてください
サイードアスラム

5

少なくとも-v3.2.14では、Railsが提供する方法でこれを行う適切な方法がわかりません。

@Sheharyar Naseerは、問題を解決するために使用できるオプションハッシュを参照しますが、彼が提案しているように私が見る限りではそうではありません。

私はこれをしました=>

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

または

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

私の場合g.object_name"gallery_set[blog_posts_attributes][2]" レンダリングされた3番目のフィールドに対してこのような文字列を返すので、その文字列のインデックスを照合して使用します。


実際には、よりクールな(そしておそらくよりクリーンな)方法は、ラムダを渡してインクリメントすることです。

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

そして、ビューで

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

1

私はこれが少し遅いことを知っていますが、最近これをしなければならなかったので、このようにfields_forのインデックスを取得できます

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

これが役に立てば幸いです:)


1

fields_for child_indexに追加:0

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

これが新しいベストアンサーです。
genkilabs

他の誰かがこれで重複したフィールドを取得しますか?

0

インデックスを制御したい場合は、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 =>

これにより

<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">

フォームが送信されると、paramsには次のようなものが含まれます

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

マルチドロップダウンを機能させるには、インデックスオプションを使用する必要がありました。幸運を。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.