Rails: fields_for พร้อมดัชนี?


103

มีวิธีการ (หรือวิธีดึงฟังก์ชันที่คล้ายกันออก) เพื่อทำfields_for_with_indexหรือไม่?

ตัวอย่าง:

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

การแสดงผลบางส่วนนั้นจำเป็นต้องรู้ว่าดัชนีปัจจุบันอยู่ในfields_forลูปอะไร


3
ฉันจะ + สิ่งนี้ไว้ในรางแกน ... ลองเจอเคสนี้ด้วย
Nathan Bertram

คำตอบ:


93

คำตอบด้านล่างถูกโพสต์ไว้เมื่อหลายปีก่อนสำหรับแนวทางที่ทันสมัยโปรดดู: https://stackoverflow.com/a/22640703/105403


นี่จะเป็นแนวทางที่ดีกว่าจริงๆโดยทำตามเอกสาร 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_ สำหรับสิ่งนี้ แต่เนื่องจากไม่มีคำตอบของคุณที่ช่วยชีวิตฉันไว้ ขอบคุณ.
Anders Kindberg

1
คุณยังสามารถใช้อ็อพชันที่ไม่มีเอกสาร: child_index บน fields_for หากคุณต้องการการควบคุมมากขึ้นว่าดัชนีใดจะแสดงผลเช่นนี้ fields_for (: projects, project, child_index: index)
Anders Kindberg

8
ผู้ใช้ Rails 4.0.2+ ควรตรวจสอบคำตอบของ Ben เนื่องจากตอนนี้ดัชนีถูกสร้างขึ้นในตัวสร้างแล้ว
notapatch

1
@Marco คุณคือราชาเซอร์ คุณช่วยฉันวันต่อวัน :-) หวังว่าฉันจะให้คุณได้ 10 + ...... !

1
คำตอบนี้ควรได้รับการแก้ไขและอัปเดตด้วยคำตอบของ Ben ตามที่ notapatch กล่าวไว้
JudgeProphet

157

คำตอบนั้นค่อนข้างง่ายเนื่องจากมีการให้คำตอบภายใน Rails คุณสามารถใช้f.optionsพารามิเตอร์ ดังนั้นภายในการแสดงผลของคุณ_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
มันไม่ทำงานที่นี่ คุณสามารถให้ลิงค์เอกสารรางได้หรือไม่?
Lucas Renan

2
@LucasRenan & @graphmeter - กรุณาอ่านคำถามอีกครั้งคุณจำเป็นต้องโทร<%= f.options[:child_index] %>ในย่อยรูปแบบการแสดงผลของคุณ (ในกรณีนี้: _some_form.html.erb) ไม่ int builderเดิม อัปเดตคำตอบเพื่อความกระจ่างเพิ่มเติม
Sheharyar

1
แปลกฉันเข้าใจnilว่า
bcackerman

1
@Sheharyar นี่ทำงานใน Rails 4 แต่นี่ให้ค่าฉันเช่น '1450681048049,1450681050158,1450681056830,1450681141951,1450681219262' แต่ฉันต้องการดัชนีในรูปแบบนี้ '1,2,3,4,5' ฉันควรทำอย่างไรดี?
vidal

2
ยอดเยี่ยม. นี่ควรเป็นคำตอบที่ยอมรับได้อย่างแน่นอน
jeffdill2

103

ตั้งแต่ 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 %>

2
project_fields.options[:child_index]นี้ทำงานได้และมีน้อยที่จะเขียนออกกว่า งั้นฉันชอบแบบนี้ดีกว่า!
Casey

17

สำหรับ Rails 4+

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

Monkey Patch For Rails 3 รองรับ

ในf.indexการทำงานใน Rails 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

เช็คเอาท์การแสดงคอลเลกชันของ partials หากความต้องการของคุณคือเทมเพลตจำเป็นต้องวนซ้ำบนอาร์เรย์และแสดงเทมเพลตย่อยสำหรับแต่ละองค์ประกอบ

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

ทำคำถาม has_many: ความคิดเห็นและวิธีสร้างความคิดเห็นเช่นในการดำเนินการใหม่หรือแก้ไขคำถามลองทำ 1.times {question.comments.build}
Syed Aslam

5

ฉันไม่เห็นวิธีที่ดีในการทำสิ่งนี้ผ่านทาง Rails ที่มีให้อย่างน้อยก็ไม่ใช่ใน -v3.2.14

@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]" สำหรับฟิลด์ที่สามที่แสดงผลดังนั้นฉันจึงจับคู่ดัชนีในสตริงนั้นและใช้มัน


จริงๆแล้ววิธีที่เย็นกว่า (และอาจจะสะอาดกว่า?) คือการส่งแลมด้าและเรียกมันว่าเพิ่มขึ้น

# /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 %>

2

เพิ่มใน 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

มีใครได้รับช่องที่ซ้ำกับสิ่งนี้หรือไม่?

1

ฉันรู้ว่ามันช้าไปหน่อย แต่ฉันเพิ่งทำสิ่งนี้คุณจะได้รับดัชนีของ fields_ สำหรับแบบนี้

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

ฉันหวังว่านี่จะช่วยได้ :)


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

หากส่งแบบฟอร์มพารามิเตอร์จะรวมสิ่งต่างๆเช่น

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

ฉันต้องใช้ตัวเลือกดัชนีเพื่อให้ multi-dropdowns ทำงาน โชคดี.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.