Rails:带有索引的 fields_for?

发布于 2024-10-15 13:32:45 字数 312 浏览 3 评论 0原文

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

示例:

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

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

Is there a method (or way to pull off similar functionality) to do a fields_for_with_index?

Example:

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

That partial being rendered needs to know what the current index is in the fields_for loop.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

菩提树下叶撕阳。 2024-10-22 13:32:45

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

The answer is quite simple as the solution is provided within Rails. You can use f.options params. So, inside your rendered _some_form.html.erb,

Index can be accessed by:

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

You don't need to do anything else.


Update: It seems that my answer wasn't clear enough...

Original HTML File:

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

Rendered Sub-Form:

<!-- _some_form.html.erb -->
<%= f.options[:child_index] %>
苏璃陌 2024-10-22 13:32:45

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

As of Rails 4.0.2, an index is now included in the FormBuilder object:

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

For example:

<%= form_for @person do |person_form| %>
  ...
  <%= person_form.fields_for :projects do |project_fields| %>
    Project #<%= project_fields.index %>
  ...
  <% end %>
  ...
<% end %>
征﹌骨岁月お 2024-10-22 13:32:45

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

The answer below was posted many years ago, for a modern approach see:
https://stackoverflow.com/a/22640703/105403


This would actually be a better approach, following Rails documentation more closely:

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

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

It’s also possible to specify the
instance to be used:

  <%= 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 %>
你爱我像她 2024-10-22 13:32:45

适用于 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 中工作,您需要向您的应用程序添加一个 Monkey 补丁项目初始值设定项将此功能添加到 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

For 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 Support

To get f.index to work in Rails 3 you need to add an monkey patch to your projects initializers to add this functionality to 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
謌踐踏愛綪 2024-10-22 13:32:45

查看渲染部分集合。如果您的要求是模板需要迭代数组并为每个元素渲染子模板。

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

希望这有帮助。

Checkout Rendering a collection of partials. If your requirement is that a template needs to iterate over an array and render a sub template for each of the elements.

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

This will render “_children.erb“ and pass the local variable 'children' to the template for display. An iteration counter will automatically be made available to the template with a name of the form partial_name_counter. In the case of the example above, the template would be fed children_counter.

Hope this helps.

娇柔作态 2024-10-22 13:32:45

我看不到通过 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 %>

I can't see a decent way to do this through the ways provided by Rails, at least not in -v3.2.14

@Sheharyar Naseer makes reference to the options hash which can be used to solve the problem but not as far as I can see in the way he seems to suggest.

I did this =>

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

or

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

In my case g.object_name returns a string like this "gallery_set[blog_posts_attributes][2]" for the third field rendered so I just match the index in that string and use it.


Actually a cooler (and maybe cleaner?) way to do it is to pass a lambda and call it to increment.

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

And the in the view

<%= 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 %>
内心荒芜 2024-10-22 13:32:45

添加到 child_index 的 fields_index:0

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

Added to 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 %>
别闹i 2024-10-22 13:32:45

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

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

我希望这有帮助:)

I know that this is a bit late but I recently had to do this you can get the index of the fields_for like this

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

I hope that this helps :)

盛夏尉蓝 2024-10-22 13:32:45

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

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

If you want to have control over the indexes check out the index option

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

This will produce

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

If the form is submitted, params will include something like

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

I had to use the index option to get my multi-dropdowns to work. Good luck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文