【问题标题】:Rails: fields_for with index?Rails:带索引的fields_for?
【发布时间】:2011-06-18 17:03:30
【问题描述】:

是否有方法(或实现类似功能的方法)来执行fields_for_with_index

例子:

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

正在渲染的部分需要知道fields_for 循环中的当前索引。

【问题讨论】:

  • 我会在核心导轨中加一个这个...继续遇到这个的案例。

标签: ruby-on-rails loops fields-for


【解决方案1】:

答案很简单,因为解决方案在 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] %>

【讨论】:

  • 这里不工作。你能提供rails文档链接吗?
  • @LucasRenan & @graphmeter - 请再次阅读问题,您需要在渲染的子表单中调用&lt;%= f.options[:child_index] %&gt;(在这种情况下:_some_form.html.erb),而不是原始@ 987654327@。已更新答案以获得更多说明。
  • 奇怪,我得到了nil
  • @Sheharyar 这是在 Rails 4 中工作的。但这给了我像 '1450681048049,1450681050158,1450681056830,1450681141951,1450681219262' 这样的值。但我需要这种形式的索引'1,2,3,4,5'。我该怎么办?
  • 太棒了。这绝对应该是公认的答案。
【解决方案2】:

从 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]写得少。所以我更喜欢这种方式!
【解决方案3】:

下面的答案是多年前发布的,有关现代方法,请参阅: 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 %>

【讨论】:

  • 我希望在 fields_for 中为此内置一些东西,但由于没有您的答案,我的一天得以保存。谢谢。
  • 如果您需要更多地控制渲染哪个索引,您还可以在 fields_for 上使用未记录的选项 :child_index,如下所示:fields_for(:projects, project, child_index: index)
  • Rails 4.0.2+ 用户应该查看 Ben 的答案,因为索引现在已内置到构建器中。
  • @Marco 你是国王先生。你拯救了我的每一天。 :-) 希望我能给你几个 10+ ......!
  • 这个答案应该像 notapatch 所说的那样用 Ben 的答案进行编辑和更新
【解决方案4】:

适用于 Rails 4+

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

支持 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 更干净。
【解决方案5】:

结帐Rendering a collection of 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 是我的部分名字...
  • Do question has_many :cmets and how are you build cmets for example in your new or edit action of questions try doing 1.times { question.cmets.build }
【解决方案6】:

我看不到通过 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]" 的字符串,因此我只需匹配该字符串中的索引并使用它。


实际上,一种更酷(也许更清洁?)的方法是传递一个 lambda 并调用它来递增。

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

【讨论】:

    【解决方案7】:

    添加到 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 %>
    

    【讨论】:

    • 这是新的最佳答案。
    • 还有其他人得到重复的字段吗?
    【解决方案8】:

    我知道这有点晚了,但我最近不得不这样做你可以像这样获取 fields_for 的索引

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

    我希望这会有所帮助:)

    【讨论】:

      【解决方案9】:

      如果您想控制索引,请查看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"
          }
        }
      }
      

      我必须使用索引选项才能让我的多下拉菜单工作。祝你好运。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多