Rafael Costa
Rafael Costa

Reputation: 1481

Rails - How to split nested attributes through the form?

I understand we can use fields_for to create a subsection of fields for nested attributes. However, I would like to split them through the form. How can I do this?

For instance:

Suppose I have a model foo with a nested bar model, like this:

    class Foo < ApplicationRecord
      has_many :bars
      accepts_nested_attributes_for :bars
    end

A general view would be something like this:

    <%= form_for @foo do |f| %>
      <!-- foo fields -->

      <%= f.fields_for :bars do |f_bar| %>
        <!-- bar fields -->
      <% end %>

      <%= f.submit "Submit" %>
    <% end %>

But for aesthetics reasons, I don't want all the bars agglomerated at one place. I would like to do something like:

    <%= form_for @foo do |f| %>
      <!-- foo fields -->

      <%= f.fields_for :bars do |f_bar| %>
        <!-- bar fields of one bar -->
      <% end %>

      <!-- other foo fields -->

      <%= f.fields_for :bars do |f_bar| %>
        <!-- bar fields of another bar -->
      <% end %>

      <!-- The previous repeats many more times in a non predictable way -->

      <%= f.submit "Submit" %>
    <% end %>

So it would be perfect for me if I didn't have to show all the bars at once. Somebody knows how to that?

Upvotes: 2

Views: 1070

Answers (2)

Rafael Costa
Rafael Costa

Reputation: 1481

So, it happens that all I needed was make fields_for show only one instance per time.

I discovered that fields_for lets you specify a particular object to render the fields. So, I just created a counter and added one @foo.bars[counter] per time and it magically worked, it was something like this :

    <% counter = 0 %>
    <%= form_for @foo do |f| %>

      <!-- foo fields -->
    
      <%= f.fields_for :bars, @foo.bars[counter] do |f_bar| %>
        <!-- bar fields of one bar -->
      <% end %>
      <% counter+=1 %>
    
      <!-- other foo fields -->
    
      <%= f.fields_for :bars, @foo.bars[counter] do |f_bar| %>
        <!-- bar fields of another bar -->
      <% end %>
      <% counter+=1 %>
    
      <!-- The previous repeats many more times in a non predictable way -->
    
      <%= f.submit "Submit" %>
    <% end %>

Upvotes: 4

rogelio
rogelio

Reputation: 1569

You can use the second param of fields_for and pass a scope:

class Bar < ApplicationRecord

  belongs_to :foo

  scope :some_a,->{where(conditions)}
  scope :some_b,->{where(conditions)}

end

In your forms

<%= form_for @foo do |f| %>
    <%= f.text_field :foo_attr %>

    <%= f.fields_for :bars, @foo.bars.some_a do |b| %>
        <%= b.hidden_field :other_bar_attr %>
        <%= b.text_field :bar_attr %>
        ...
    <% end %>

    <%= f.fields_for :bars, @foo.bars.some_b do |b| %>
        <%= b.hidden_field :other_bar_attr %>
        <%= b.text_field :bar_attr %>
        ...
    <% end %>
    <%= f.submit %>
<% end %>

You can use the hidden input for setting default values that are used in the scopes.

UPDATE

If you need to use a multiple instances of fields_for in your form, you can do something like this

In the controller set an array for the scoped objects, an example could be:

class SomeController < AP
  def some_action
    @var_to_the_form = []
    (1..well_know_quantity).each do |value|
      @var_to_the_form << Model.where(conditions)
    end
  end
end

And your form has to be as follows

<% @var_to_the_form.each do |records| %>
  <%= f.fields_for :bars, records do |b| %>
    <%= b.hidden_field :other_bar_attr %>
    <%= b.text_field :bar_attr %>
        ...
  <% end %>
<% end %>

The important part is to know how to set the records you are passing to the view.

Upvotes: 1

Related Questions