Rails: fields_for có chỉ mục?


102

Có phương pháp (hoặc cách nào để loại bỏ chức năng tương tự) để thực hiện fields_for_with_indexkhông?

Thí dụ:

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

Một phần được kết xuất cần biết chỉ mục hiện tại trong fields_forvòng lặp là gì.


3
Tôi đã + một cái này trong đường ray lõi ... Hãy tiếp tục xem qua một trường hợp cho cái này.
Nathan Bertram

Câu trả lời:


92

Đây thực sự sẽ là một cách tiếp cận tốt hơn, theo dõi kỹ hơn tài liệu 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 %>

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

Cũng có thể chỉ định phiên bản được sử dụng:

  <%= 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
Tôi ước có một cái gì đó được tích hợp vào fields_for cho việc này, nhưng vì không có câu trả lời nào của bạn đã lưu trong ngày của tôi. Cảm ơn.
Anders Kindberg

1
Bạn cũng có thể sử dụng tùy chọn không có giấy tờ: CHILD_INDEX trên fields_for, nếu bạn cần kiểm soát tốt hơn các chỉ số được trả lại, như thế này: fields_for (: dự án, dự án, CHILD_INDEX: index)
Anders kindberg

Đây là liên kết API Rails đang hoạt động api.rubyonrails.org/classes/ActionView/Helpers/…
Archonic

7
Người dùng Rails 4.0.2+ nên xem câu trả lời của Ben vì chỉ mục hiện đã được tích hợp vào trình tạo.
notapatch

1
@Marco Bạn là vua thưa Ngài. bạn đã cứu tôi ngày này sang ngày khác. :-) Ước gì tôi có thể cho bạn vài điểm 10 + ......!

157

Câu trả lời là khá đơn giản vì giải pháp được cung cấp trong Rails. Bạn có thể sử dụng f.optionsparams. Vì vậy, bên trong kết xuất của bạn _some_form.html.erb,

Chỉ mục có thể được truy cập bằng:

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

Bạn không cần phải làm bất cứ điều gì khác.


Cập nhật: Có vẻ như câu trả lời của tôi không đủ rõ ràng ...

Tệp HTML gốc:

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

Biểu mẫu phụ được hiển thị:

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

10
nó không hoạt động ở đây. bạn có thể cung cấp liên kết tài liệu rails không?
Lucas Renan

2
@LucasRenan & @graphmeter - Vui lòng đọc lại câu hỏi, bạn cần gọi <%= f.options[:child_index] %>trong biểu mẫu con được hiển thị của mình (Trong trường hợp này là: _some_form.html.erb), không phải int gốc builder. Cập nhật câu trả lời để làm rõ hơn.
Sheharyar

1
Kỳ lạ, tôi hiểu nilcho điều đó
bcackerman

1
@Sheharyar Điều này đang hoạt động trong Rails 4. Nhưng điều này đang cung cấp cho tôi các giá trị như '1450681048049,1450681050158,1450681056830,1450681141951,1450681219262'. Nhưng tôi cần chỉ mục ở dạng này '1,2,3,4,5'. Tôi nên làm gì?
vidal

2
Xuất sắc. Đây hoàn toàn nên là câu trả lời được chấp nhận.
jeffdill2

100

Kể từ Rails 4.0.2, một chỉ mục hiện được bao gồm trong đối tượng FormBuilder:

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

Ví dụ:

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

Điều này hoạt động và ít phải viết ra hơn project_fields.options[:child_index]. Vì vậy, tôi thích cách này hơn!
Casey

16

Đối với Rails 4+

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

Hỗ trợ Monkey Patch cho Rails 3

Để f.indexlàm việc trong Rails 3, bạn cần thêm một bản vá khỉ vào bộ khởi tạo dự án của mình để thêm chức năng này vàofields_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

Câu trả lời tốt hơn cho đến nay, sử dụng .indexsạch hơn nhiều.
Lucas Andrade

7

Checkout Hiển thị một bộ sưu tập các phần . Nếu yêu cầu của bạn là một mẫu cần phải lặp lại trên một mảng và hiển thị mẫu con cho từng phần tử.

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

Thao tác này sẽ hiển thị “_children.erb” và chuyển biến cục bộ 'con' vào mẫu để hiển thị. Một bộ đếm lặp lại sẽ tự động được cung cấp cho mẫu với tên của biểu mẫu partial_name_counter. Trong trường hợp của ví dụ trên, mẫu sẽ được cung cấp children_counter.

Hi vọng điêu nay co ich.


Tôi nhận được "biến cục bộ không xác định hoặc phương thức 'question_comment_form_counter'" khi làm điều đó. question_comment_formlà tên một phần của tôi ...
Shpigford

Do câu hỏi has_many: ý kiến và làm thế nào bạn xây dựng ý kiến ví dụ như trong bạn hành động mới hoặc chỉnh sửa các câu hỏi cố gắng làm 1.times {question.comments.build}
Syed Aslam

5

Tôi không thể thấy một cách hợp lý nào để làm điều này thông qua các cách được cung cấp bởi Rails, ít nhất là không phải trong -v3.2.14

@Sheharyar Naseer đề cập đến các tùy chọn băm có thể được sử dụng để giải quyết vấn đề nhưng không xa như tôi có thể thấy theo cách mà anh ấy có vẻ đề xuất.

Tôi đã làm điều này =>

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

hoặc là

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

Trong trường hợp của tôi, g.object_nametrả về một chuỗi như thế này"gallery_set[blog_posts_attributes][2]" cho trường thứ ba được hiển thị, vì vậy tôi chỉ cần khớp chỉ mục trong chuỗi đó và sử dụng nó.


Trên thực tế, một cách làm mát hơn (và có thể sạch hơn?) Là chuyển một lambda và gọi nó để tăng dần.

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

Và trong khung cảnh

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

Tôi biết rằng điều này là hơi muộn nhưng gần đây tôi đã phải làm điều này, bạn có thể nhận được chỉ mục của các fields_for như thế này

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

Tôi hy vọng rằng điều này sẽ giúp :)


1

Đã thêm vào 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 %>

Đây là câu trả lời mới nhất.
genkilabs

Bất kỳ ai khác nhận được các trường trùng lặp với điều này?

0

Nếu bạn muốn có quyền kiểm soát các chỉ mục, hãy xem indextùy chọ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 =>

Điều này sẽ tạo ra

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

Nếu biểu mẫu được gửi, các thông số sẽ bao gồm những thứ như

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

Tôi đã phải sử dụng tùy chọn chỉ mục để làm cho nhiều trình đơn thả xuống của mình hoạt động. Chúc may mắn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.